(function() {
  var PathScanner, PathSearcher, fork, kill, makeTask, path, scan, search, searchMain, split, _;

  _ = require('underscore');

  PathSearcher = require('./path-searcher');

  PathScanner = require('./path-scanner');

  path = require("path");

  split = require("split");

  /*
  Multiprocess.
  
  This is an experiment. The hope was that it would be faster than the single
  process search. However, it tuned out to be way more complicated, unreliable,
  AND slower. Not good.
  
  NOTES:
  
    * This uses stdin/out to communicate between processes. I tried
      process.send, but it often the child process would not receive the messages!
  
    * Knowing when to kill the processes is hard. There is a lot of complication
      in the main process to keep track of the state of each child process so we
      know when to halt.
  
    * Sometimes on large files (right now, minified less.js) on large searches
      (16k files) seem to make it not complete.
  */


  makeTask = function(fn) {
    return "(" + (fn.toString()) + ")();";
  };

  fork = function(task, env) {
    var args, child_process;
    child_process = require('child_process');
    args = [task, '--harmony_collections'];
    return child_process.fork('--eval', args, {
      env: env,
      cwd: __dirname,
      silent: true
    });
  };

  kill = function(childProcess) {
    childProcess.removeAllListeners();
    childProcess.kill();
    return null;
  };

  scan = function() {
    var PATHS_TO_SEARCH, STOP_CHAR, emitEnd, emitPaths, options, paths, scanner;
    PathScanner = require('./path-scanner');
    PATHS_TO_SEARCH = 50;
    STOP_CHAR = String.fromCharCode(0);
    emitPaths = function(paths) {
      return process.stdout.write(JSON.stringify(paths) + '\n');
    };
    emitEnd = function() {
      return process.stdout.write(STOP_CHAR + '\n');
    };
    options = {
      pathToScan: process.env.pathToScan,
      showHidden: process.env.showHidden === 'true',
      excludeVcsIgnores: process.env.excludeVcsIgnores === 'true'
    };
    scanner = new PathScanner(options.pathToScan, options);
    paths = [];
    scanner.on('path-found', function(path) {
      paths.push(path);
      if (paths.length === PATHS_TO_SEARCH) {
        emitPaths(paths);
        return paths = [];
      }
    });
    scanner.on('finished-scanning', function() {
      if (paths.length) {
        emitPaths(paths);
      }
      return emitEnd();
    });
    return scanner.scan();
  };

  search = function() {
    var END_CHAR, START_CHAR, STOP_CHAR, emitEnd, emitResults, emitStart, options, readLines, regex, searcher, searches, soFar;
    PathSearcher = require('./path-searcher');
    searches = 0;
    START_CHAR = String.fromCharCode(1);
    END_CHAR = String.fromCharCode(4);
    STOP_CHAR = String.fromCharCode(0);
    emitStart = function() {
      return process.stdout.write(START_CHAR + '\n');
    };
    emitResults = function(results) {
      var res;
      if (results) {
        results = ((function() {
          var _i, _len, _results;
          _results = [];
          for (_i = 0, _len = results.length; _i < _len; _i++) {
            res = results[_i];
            _results.push(JSON.stringify(res));
          }
          return _results;
        })()).join('\n');
        process.stdout.write(results + '\n');
      }
      return emitEnd();
    };
    emitEnd = function() {
      return process.stdout.write(END_CHAR + '\n');
    };
    options = process.env;
    searcher = new PathSearcher();
    regex = new RegExp(options.search, 'gi');
    soFar = '';
    readLines = function(stdin) {
      var buf, pieces;
      buf = stdin.read();
      if (!buf) {
        return null;
      }
      pieces = (soFar + buf).split('\n');
      soFar = pieces.pop();
      return pieces;
    };
    return process.stdin.on('readable', function() {
      var line, lines, paths, _i, _len, _results;
      lines = readLines(process.stdin);
      if (!lines) {
        return;
      }
      _results = [];
      for (_i = 0, _len = lines.length; _i < _len; _i++) {
        line = lines[_i];
        emitStart();
        if (!line || line[0] === STOP_CHAR) {
          _results.push(emitEnd());
        } else {
          paths = JSON.parse(line);
          _results.push(searcher.searchPaths(regex, paths, function(results) {
            return emitResults(results);
          }));
        }
      }
      return _results;
    });
  };

  searchMain = function(options) {
    var END_CHAR, START_CHAR, STOP_CHAR, end, env, finished, maybeEnd, pathCount, resultCount, scanFinished, scanProcess, scanTask, searchProcess, searchTask, searches;
    options.pathToScan = path.resolve(options.pathToScan);
    env = _.extend({}, process.env, options);
    START_CHAR = String.fromCharCode(1);
    END_CHAR = String.fromCharCode(4);
    STOP_CHAR = String.fromCharCode(0);
    scanTask = makeTask(scan);
    searchTask = makeTask(search);
    console.time('Multi Process Search');
    searches = 0;
    finished = false;
    scanFinished = false;
    resultCount = 0;
    pathCount = 0;
    scanProcess = fork(scanTask, env);
    searchProcess = fork(searchTask, env);
    searchProcess.stdin.setEncoding = 'utf-8';
    scanProcess.stdout.pipe(split()).on('data', function(data) {
      if (data[0] === STOP_CHAR) {
        scanFinished = true;
        return maybeEnd();
      } else {
        searches++;
        return searchProcess.stdin.write(data + '\n');
      }
    });
    searchProcess.stderr.pipe(process.stderr);
    searchProcess.stdout.pipe(split()).on('data', function(line) {
      var results;
      if (line[0] === START_CHAR) {
        if (options.verbose) {
          console.log("search start");
        }
      } else if (line[0] === END_CHAR) {
        searches--;
        if (options.verbose) {
          console.log("search end " + searches);
        }
      } else if (line && line.length) {
        results = JSON.parse(line);
        if (results) {
          pathCount++;
          resultCount += results.matches.length;
          if (options.verbose) {
            console.log("" + results.matches.length + " matches in " + results.path);
          }
        }
      }
      return maybeEnd();
    });
    scanProcess.on('message', function(_arg) {
      var arg, event;
      event = _arg.event, arg = _arg.arg;
      return console.log(event, arg);
    });
    searchProcess.on('message', function(_arg) {
      var arg, event;
      event = _arg.event, arg = _arg.arg;
      return console.log(event, arg);
    });
    maybeEnd = function() {
      if (finished) {
        return;
      }
      if (scanFinished && searches === 0) {
        kill(scanProcess);
        kill(searchProcess);
        return end();
      }
    };
    return end = function() {
      finished = true;
      console.timeEnd('Multi Process Search');
      return console.log("" + resultCount + " matches in " + pathCount + " files");
    };
  };

  module.exports = {
    searchMain: searchMain
  };

}).call(this);
