
'use strict';

const EventEmitter  = require('events').EventEmitter;
const sane = require('sane');
const Promise = require('promise');
const exec = require('child_process').exec;

const MAX_WAIT_TIME = 25000;

const detectingWatcherClass = new Promise(function(resolve) {
  exec('which watchman', function(err, out) {
    if (err || out.length === 0) {
      resolve(sane.NodeWatcher);
    } else {
      resolve(sane.WatchmanWatcher);
    }
  });
});

let inited = false;

class FileWatcher extends EventEmitter {

  constructor(rootConfigs) {
    if (inited) {
      throw new Error('FileWatcher can only be instantiated once');
    }
    inited = true;

    super();
    this._watcherByRoot = Object.create(null);

    this._loading = Promise.all(
      rootConfigs.map(createWatcher)
    ).then(watchers => {
      watchers.forEach((watcher, i) => {
        this._watcherByRoot[rootConfigs[i].dir] = watcher;
        watcher.on(
          'all',
          // args = (type, filePath, root, stat)
          (...args) => this.emit('all', ...args)
        );
      });
      return watchers;
    });

    this._loading.done();
  }

  getWatchers() {
    return this._loading;
  }

  getWatcherForRoot(root) {
    return this._loading.then(() => this._watcherByRoot[root]);
  }

  isWatchman() {
    return detectingWatcherClass.then(
      Watcher => Watcher === sane.WatchmanWatcher
    );
  }

  end() {
    return this._loading.then(
      (watchers) => watchers.map(
        watcher => Promise.denodeify(watcher.close).call(watcher)
      )
    );
  }

  static createDummyWatcher() {
    const ev = new EventEmitter();
    ev.end = function() {
      return Promise.resolve();
    };
    return ev;
  }
}

function createWatcher(rootConfig) {
  return detectingWatcherClass.then(function(Watcher) {
    const watcher = new Watcher(rootConfig.dir, {
      glob: rootConfig.globs,
      dot: false,
    });

    return new Promise(function(resolve, reject) {
      const rejectTimeout = setTimeout(function() {
        reject(new Error([
          'Watcher took too long to load',
          'Try running `watchman version` from your terminal',
          'https://facebook.github.io/watchman/docs/troubleshooting.html',
        ].join('\n')));
      }, MAX_WAIT_TIME);

      watcher.once('ready', function() {
        clearTimeout(rejectTimeout);
        resolve(watcher);
      });
    });
  });
}

module.exports = FileWatcher;
