var _ = require('underscore');
var Fiber = require('fibers');
var Future = require('fibers/future');
var runLog = require('./run-log.js');
var utils = require('./utils.js');

// options: runner url browserId xunitOutputFile
var Selenium = function (options) {
  var self = this;
  options = options || {};

  self.driver = null;
  self.server = null;

  self.browserId = options.browserId || 'xunit';
  self.url = options.url || 'http://localhost:3000/' + self.browserId;
  self.xunitOutputFile = options.xunitOutputFile || 'test-results.xml';

  self.runner = options.runner;
  self.browser = options.browser || 'chrome';

  self.xunitLines = null;
};

var _promiseToFuture = function (promise) {
  var fut = new Future;
  promise.then(function (result) {
    fut.isResolved() || fut['return'](result);
  }, function (err) {
    fut.isResolved() || fut['throw'](err);
  });
  return fut;
};


// The magic prefix for special log output
// Must match packages/test-in-console/driver.js
var MAGIC_PREFIX = '##_meteor_magic##';

// For some reason, we can't see the console.log output
// unless we 'flush' it by sending another console.log via execute()
// Note that this is actually a magic message, so we get it echoed back to us;
// that's not necessary for this to work, but it keeps the output clean for users.
var DUMMY_FLUSH = MAGIC_PREFIX + "flush: flush";

_.extend(Selenium.prototype, {
  // Start the selenium server, block (yield) until it is ready to go
  // (actively listening on outer and proxying to inner), and then
  // return.
  start: function () {
    var self = this;

    if (self.server)
      throw new Error("already running?");

    self.xunitLines = [];

    var webdriver = require('selenium-webdriver');

    var capabilities;
    var loggingPrefs;
    if (self.browser === 'chrome') {
      capabilities = webdriver.Capabilities.chrome();
      loggingPrefs = {'browser': 'ALL'};
    } else if (self.browser === 'firefox') {
      capabilities = webdriver.Capabilities.firefox();
      loggingPrefs = {'browser': 'ALL'};
    } else {
      throw new Error("Unhandled browser: " + self.browser);
    }

    if (loggingPrefs) {
      capabilities = capabilities.set('loggingPrefs', loggingPrefs);
    }

    var builder = new webdriver.Builder().withCapabilities(capabilities);
    self.driver = builder.build();

    var fut = _promiseToFuture(self.driver.getSession());
    fut.wait();

    _promiseToFuture(self.driver.get(self.url)).wait();

    Fiber(function () {
      try {
        self._pollLogs();
      } catch (err) {
        runLog.log("Log polling exited unexpectedly: " + err);
      }
    }).run();
  },

  stop: function () {
    var self = this;

    if (! self.driver)
      return;

    _promiseToFuture(self.driver.close()).wait();
    _promiseToFuture(self.driver.quit()).wait();
    self.driver = null;
  },

  _flushLogs: function () {
    var self = this;

    var promise = self.driver.executeScript("console.log('" + DUMMY_FLUSH + "');", []);
    _promiseToFuture(promise).wait();
  },

  _getLogs: function () {
    var self = this;

    var promise = self.driver.manage().logs().get('browser');
    return _promiseToFuture(promise).wait();
  },

  _gotStateDone: function () {
    var self = this;

    if (self.xunitOutputFile) {
      runLog.log("Writing xunit output to: " + self.xunitOutputFile);
      files.writeFile(self.xunitOutputFile, self.xunitLines.join('\n'));
    }

    if (self.runner) {
      runLog.log("Shutting down in response to 'done' state");
      self.runner.stop();
      process.exit(0);
    }
  },

  _gotState: function (state) {
    var self = this;

    runLog.log("State -> " + state);

    if (state === "done") {
      self._gotStateDone();
    }
  },

  _gotMagicLog: function (facility, msg) {
    var self = this;

    if (facility == 'xunit') {
      self.xunitLines.push(msg);
    } else if (facility == 'state') {
      self._gotState(msg);
    } else if (facility == 'flush') {
      // Ignore
    } else {
      runLog.log("Unknown magic: " + facility + ": " + msg);
    }
  },

  _pollLogsOnce: function () {
    var self = this;

    self._flushLogs();
    var logs = self._getLogs();
    _.each(logs, function (log) {
      var msg = log.message;
      var regex = /([^\s]*)\s*([^\s]*)\s*(.*)/i;
      var match = regex.exec(msg);
      if (!match) {
        runLog.log("Unknown console.log message format: " + JSON.stringify(log));
        return;
      }
      msg = match[3];
      if (msg === DUMMY_FLUSH) return;
      if (msg.indexOf(MAGIC_PREFIX) == 0) {
        msg = msg.substring(MAGIC_PREFIX.length);
        var colonIndex = msg.indexOf(': ');
        if (colonIndex == -1) {
          self._gotMagicLog('', msg);
        } else {
          var facility = msg.substring(0, colonIndex);
          msg = msg.substring(colonIndex + 2);
          self._gotMagicLog(facility, msg);
        }
      } else {
        runLog.log(msg);
      }
    });
  },

  _pollLogs: function () {
    var self = this;

    while (self.driver) {
      try {
        self._pollLogsOnce();
      } catch (err) {
        runLog.log("Error reading console log: " + err);
      }
      utils.sleepMs(1000);
    }
  },
});

exports.Selenium = Selenium;

@@ -90,24 +90,59 @@
 s/future');%0A
+var files = require('./files.js');%0A
 var runLog =

Add files import to tools/run-selenium.js