'use strict'
let fs = require('fs')
// simplest version-- synchronous forEach and async readFile and writeFile
function copyAll1(src, dest, callback) {
  let count = src.length
  src.forEach(filename =>
    fs.readFile(filename, (err, data) =>
      fs.writeFile(dest + '/' + filename, data, () => --count || callback())))
}
// if we write the loop ourself, we need a local variable that is captured
// instead of i that will change and callback will refer to incorrect value of i
function copyAll2(src, dest, callback) {
  let count = src.length
  for (let i = 0; i < src.length; ++i) {
    let filename = src[i]
    fs.readFile(filename, (err, data) =>
      fs.writeFile(dest + '/' + filename, data, () => --count || callback()))
  }
}
// using for/of automatically takes care of this by giving no index variable
function copyAll3(src, dest, callback) {
  let count = src.length
  for (let filename of src) {
    fs.readFile(filename, (err, data) =>
      fs.writeFile(dest + '/' + filename, data, () => --count || callback()))
  }
}
// we can also move the body into an anonymous function and pass src[i] this is 
// interesting only to see lambdas working but is an overkill for this problem
function copyAll4(src, dest, callback) {
  let count = src.length
  for (let i = 0; i < src.length; ++i) {
    (filename =>
      fs.readFile(filename, (err, data) =>
        fs.writeFile(dest + '/' + filename, data, () =>
          --count || callback())))(src[i])
  }
}
// if we use recursion and process elements one by one we do not need a counter but 
// there is no parallel I/O so this is a bad solution
function copyAll5(src, dest, callback) {
  if (src.length) {
    const filename = src.shift()
    fs.readFile(filename, (err, data) =>
      fs.writeFile(dest + '/' + filename, data, () => copyAll5(src, dest, callback)))
  } else {
    callback()
  }
}
// if we use recursion to start all I/O in parallel we need to pass count by 
// reference and only objects (arrays, maps) are passed by reference 
function copyAll6(src, dest, callback, count) {
  if (!count) { // if we dont pass count, it will be undefined and this case runs
    count = { val: src.length } // this is an object with one property 'val'
  }
  if (src.length) {
    const filename = src.shift()
    fs.readFile(filename, (err, data) =>
      fs.writeFile(dest + '/' + filename, data, () => --count.val || callback()))
    copyAll6(src, dest, callback, count)
  }
}
fs = require('mz/fs') // reset fs to library that uses promises
// using Promise.all to wait for all read/write pairs to finish
function copyAll7(src, dest, callback) {
  Promise.all(
    src.map(filename =>
      fs.readFile(filename)
      .then(data => fs.writeFile(dest + '/' + filename, data))))
  .then(callback)
}
// returning a promise instead of using a callback
function copyAll8(src, dest) {
  return Promise.all(
    src.map(filename =>
      fs.readFile(filename)
      .then(data => fs.writeFile(dest + '/' + filename, data))))
}
const co = require('co') // library to use generator functions as coroutines
// this is a generator function that we have yet to study
function* copyAll9(src, dest) {
  return src.map(filename => co(function* () {
    const data = yield fs.readFile(filename)
    fs.writeFile(dest + '/' + filename, data)
  }))
}
// one final way is to use readFileSync but we discard this solution 
// since it means NOTHING ELSE can run until we get data from disk
function copyAll10(src, dest) {
  return src.forEach(filename => {
    const data = fs.readFileSync(filename)
    fs.writeFileSync(dest + '/' + filename, data)
  })
}

// the following will run correctly if the files are present in current directory 
// and a subdirectory with the name mydestdir exists. we did not check for errors
copyAll1(['test2.js', 'test4.js'], 'mydestdir', () => console.log('All done'))

// the promises version is called like this
//   copyAll8(['test2.js', 'test4.js'], 'mydestdir')
//   .then(() => console.log('All done'))

// the generator version is called like this
//   co(copyAll9(['test2.js', 'test4.js'], 'mydestdir'))
//   .then(() => console.log('All done'))