'use strict';
var path = require('path');
var fs = require('fs');
var glob = require('glob');
var mkdirp = require('mkdirp');
var through = require('through2').obj;
var noms = require('noms').obj;
function toStream(array) {
  var length = array.length;
  var i = 0;
  return noms(function (done) {
    if (i >= length) {
      this.push(null);
    }
    this.push(array[i++]);
    done();
  });
}
function depth(string) {
  return path.normalize(string).split(path.sep).length - 1;
}
function dealWith(inPath, up) {
  if (!up) {
    return inPath;
  }
  if (up === true) {
    return path.basename(inPath);
  }
  if (depth(inPath) < up) {
    throw new Error('cant go up that far');
  }
  return path.join.apply(path, path.normalize(inPath).split(path.sep).slice(up));
}
var copyFile = _copyFile;
function _copyFile (src, dst, opts, callback) {
  fs.createReadStream(src)
    .pipe(fs.createWriteStream(dst, {
      mode: opts.mode
    }))
    .once('error', callback)
    .once('finish', function () {
      fs.chmod(dst, opts.mode, function (err) {
        callback(err);
      })
    })
}
if (fs.copyFile) {
  copyFile = function (src, dst, opts, callback) {
    fs.copyFile(src, dst, callback);
  }
}
function makeDebug(config) {
  if (config.verbose) {
    return function (thing) {
      console.log(thing);
    }
  }
  return function () {}
}
module.exports = copyFiles;
function copyFiles(args, config, callback) {
  if (typeof config === 'function') {
    callback = config;
    config = {
      up:0
    };
  }
  if (typeof config !== 'object' && config) {
    config = {
      up: config
    };
  }
  var debug = makeDebug(config);
  var copied = false;
  var opts = config.up || 0;
  var soft = config.soft;
  if (typeof callback !== 'function') {
    throw new Error('callback is not optional');
  }
  var input = args.slice();
  var outDir = input.pop();
  var globOpts = {};
  if (config.exclude) {
    globOpts.ignore = config.exclude;
  }
  if (config.all) {
    globOpts.dot = true;
  }
  if (config.follow) {
    globOpts.follow = true;
  }
  toStream(input)
  .pipe(through(function (pathName, _, next) {
    var self = this;
    glob(pathName, globOpts, function (err, paths) {
      if (err) {
        return next(err);
      }
      paths.forEach(function (unglobbedPath) {
        debug(`unglobed path: ${unglobbedPath}`);
        self.push(unglobbedPath);
      });
      next();
    });
  }))
  .on('error', callback)
  .pipe(through(function (pathName, _, next) {
    fs.stat(pathName, function (err, pathStat) {
      if (err) {
        return next(err);
      }
      var outName = path.join(outDir, dealWith(pathName, opts));
      function done(){
        mkdirp(path.dirname(outName)).then(()=>{
          next(null, {
            pathName: pathName,
            pathStat: pathStat
          });
        }, next);
      }
      if (pathStat.isDirectory()) {
        debug(`skipping, is directory: ${pathName}`)
        return next();
      }
      if (!pathStat.isFile()) {
        return next(new Error('how can it be neither file nor folder?'))
      }
      if (!soft) {
        return done();
      }
      fs.stat(outName, function(err){
        if(!err){
          //file exists
          return next()
        }
        if (err.code === 'ENOENT') {
          //file does not exist
          return done();
        }
        // other error
        return next(err)
      })
    });
  }))
  .on('error', callback)
  .pipe(through(function (obj, _, next) {

    if (!copied) {
      copied = true;
    }
    var pathName = obj.pathName;
    var pathStat = obj.pathStat;
    var outName = path.join(outDir, dealWith(pathName, opts));
    debug(`copy from: ${pathName}`)
    debug(`copy to: ${outName}`)
    copyFile(pathName, outName, pathStat, next)
  }))
  .on('error', callback)
  .on('finish', function () {
    if (config.error && !copied) {
      return callback(new Error('nothing coppied'));
    }
    callback();
  });
}
