var retrocycle = require('cycle').retrocycle;
var fork = require('child_process').fork;

exports = module.exports = mochaParallel;

/**
 * Callback definition for mochaParallel
 *
 * @callback mochaParallelCallback
 * @param {object} rootSuite - An integer.
 */

/**
 * Runs the specified Mocha test files in parallel
 *
 * @example
 * var files = [ 'test1.js', 'test2.js' ];
 * var options = {
 *   setup: 'setup.js',
 *   mochaOptions: {
 *     reporter: 'tap',
 *     timeout: 10e3
 *   }
 * };
 * mochaParallel(files, options, function(rootSuite) {
 *   console.log('ALL DONE');
 *   console.log(rootSuite.suites);
 * });
 *
 * @param {string[]} files - Array of tests' filenames
 * @param {Object} options
 * @param {string} [options.setup] - Optional file to be run before each test file
 * @param {Object} [options.mochaOptions] - Optional options to pass to Mocha's JS API
 *   @see {@link https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options}
 * @param {mochaParallelCallback} callback - Called when all test have completed
 */
function mochaParallel(files, options, callback) {
  var forks = files.length;
  var rootSuite = {
    root: true,
    suites: []
  };

  console.log();

  files.forEach(function (file) {
    var suites;
    var runner = fork(__dirname + '/runner.js', { silent: true });

    runner.stderr.pipe(process.stderr);

    runner.on('error', function (error) {
      console.error('Error executing file', file);
    });

    runner.on('message', function (fileRootSuite) {
      suites = retrocycle(fileRootSuite).suites;
    });

    // Buffer stdout to avoid intermixing with other forks
    var stdout = [];
    runner.stdout.on('data', function (data) {
      stdout.push(data);
    });

    runner.on('close', function () {
      console.log(stdout.join(''));

      suites.forEach(function (suite) {
        suite.parent = rootSuite;
        rootSuite.suites.push(suite);
      });

      forks--;
      if (!forks) {
        return callback(rootSuite);
      }
    });

    // Begin testing
    runner.send({
      file: file,
      setup: options.setup,
      options: options.mochaOptions
    });
  });
}

@@ -1244,24 +1244,325 @@
 file) %7B%0A    
+test(file, options, function (suites) %7B%0A      suites.forEach(function (suite) %7B%0A        suite.parent = rootSuite;%0A        rootSuite.suites.push(suite);%0A      %7D);%0A%0A      forks--;%0A      if (!forks) %7B%0A        return callback(rootSuite);%0A      %7D%0A    %7D)%0A  %7D);%0A%7D%0A%0Afunction test(file, options, callback) %7B%0A  
 var suites;%0A
@@ -1561,18 +1561,16 @@
 suites;%0A
-  
   var ru
@@ -1623,26 +1623,24 @@
 : true %7D);%0A%0A
-  
   runner.std
@@ -1662,26 +1662,24 @@
 s.stderr);%0A%0A
-  
   runner.on(
@@ -1702,34 +1702,32 @@
 n (error) %7B%0A    
-  
 console.error('E
@@ -1755,27 +1755,23 @@
  file);%0A
-  
   %7D);%0A%0A
-  
   runner
@@ -1808,26 +1808,24 @@
 ootSuite) %7B%0A
-  
     suites =
@@ -1859,27 +1859,23 @@
 suites;%0A
-  
   %7D);%0A%0A
-  
   // Buf
@@ -1923,18 +1923,16 @@
 r forks%0A
-  
   var st
@@ -1942,18 +1942,16 @@
 t = %5B%5D;%0A
-  
   runner
@@ -1991,18 +1991,16 @@
 ) %7B%0A    
-  
 stdout.p
@@ -2010,27 +2010,23 @@
 (data);%0A
-  
   %7D);%0A%0A
-  
   runner
@@ -2048,26 +2048,24 @@
 nction () %7B%0A
-  
     console.
@@ -2091,170 +2091,8 @@
 );%0A%0A
-      suites.forEach(function (suite) %7B%0A        suite.parent = rootSuite;%0A        rootSuite.suites.push(suite);%0A      %7D);%0A%0A      forks--;%0A      if (!forks) %7B%0A    
     
@@ -2111,39 +2111,24 @@
 ack(
-rootS
+s
 uite
+s
 );%0A
-      %7D%0A  
   %7D);%0A%0A
-  
   //
@@ -2142,18 +2142,16 @@
 testing%0A
-  
   runner
@@ -2158,18 +2158,16 @@
 .send(%7B%0A
-  
     file
@@ -2178,18 +2178,16 @@
 le,%0A    
-  
 setup: o
@@ -2200,18 +2200,16 @@
 .setup,%0A
-  
     opti
@@ -2234,24 +2234,16 @@
 Options%0A
-    %7D);%0A
   %7D);%0A%7D%0A

Move per-file code into separate function