
var Promise = require('es6-promise').Promise;
var spawn = require('child_process').spawn;
var fs = require('fs');
var net = require('net');
var util = require('util');
var EventEmiter = require('events').EventEmitter;
var mongo = require('./mongo');
var tools = require('./tools');
var mongoServer = null;
var config = tools.getConfig();

module.exports = Gagarin;
  
function Gagarin (options) {
  options = options || {};
  
  if (!mongoServer) {
    // only do it once
    mongoServer = new mongo.Server(config);
  }
  
  return new GagarinAsPromise(mongoServer.then(function (handle) {

    return new Promise(function (resolve, reject) {
      // add timeout ??
      
      var port = 4000 + Math.floor(Math.random() * 1000);
      var name = 'gagarin_' + Date.now();

      process.env.MONGO_URL = 'mongodb://localhost:' + handle.port + '/' + name;
      process.env.ROOT_URL  = 'http://localhost:' + port;
      process.env.PORT      = port;

      var meteor = spawn('node', [ options.pathToApp ], { env: process.env });
      var gagarin = null;
      
      meteor.stdout.on('data', function (data) {
        var match;
        if (!gagarin) {
          match = /Gagarin listening at port (\d+)/.exec(data.toString());
          if (match) {
            gagarin = new Transponder(meteor, { port: parseInt(match[1]), cleanUp: function () {
              return mongo.connect(mongoServer, name).then(function (db) {
                return db.drop();
              });
            }});
            resolve(gagarin);
          }
        }
      });

      // TODO: only log in verbose mode
      meteor.stderr.on('data', function (data) {
        console.error(data.toString());
      });

    });

  }));
}

Gagarin.config = function (options) {
  Object.keys(options).forEach(function (key) {
    config[key] = options[key];
  });
};

// GAGARIN AS PROMISE

function GagarinAsPromise (operand, promise) {
  this._operand = operand;
  this._promise = promise || operand;
}

GagarinAsPromise.prototype.sleep = function (timeout) {
  var self = this;
  return self.then(function () {
    return new Promise(function (resolve) {
      setTimeout(resolve, timeout);
    });
  });
};

// proxies for promise methods

[ 'then', 'catch' ].forEach(function (name) {
  GagarinAsPromise.prototype[name] = function () {
    return new GagarinAsPromise(this._operand, this._promise[name].apply(this._promise, arguments));
  }
});

// proxies for transponder methods

[ 'eval', 'kill' ].forEach(function (name) {
  GagarinAsPromise.prototype[name] = function () {
    var args = Array.prototype.slice.call(arguments, 0);
    var self = this;
    return new GagarinAsPromise(self._operand, Promise.all([ self._operand, self._promise ]).then(function (all) {
      return all[0][name].apply(all[0], args);
    }));
  };
});

// GAGARIN API

function Transponder(meteor, options) {

  // iherit from EventEmitter
  EventEmiter.call(this);

  var self = this;
  var connect = new Promise(function (resolve, reject) {
    var socket = net.createConnection(options.port, function () {
      resolve(socket);
    });

    // listen to emit events
    socket.setEncoding('utf8');
    socket.on('data', function (data) {
      try {
        data = JSON.parse(data);
      } catch (err) {
        return; // ignore?
      }
      if (data.name) {
        self.emit(data.name, data.value);
      }
    });
  });

  self.eval = function (code) {
    var args = Array.prototype.slice.call(arguments, 1);
    var name = uniqe().toString();

    return connect.then(function (socket) {
      socket.write(JSON.stringify({
          name: name,
          code: code.toString(),
          args: args,
        }), function () {
          // do we need this callback (?)
        });

      return new Promise(function (resolve, reject) {
        self.once(name, function (value) {
          resolve(value);
        });
        // reject? timeout?
      });

    });
  };

  self.kill = function () {
    return Promise.all([

      options.cleanUp(),

      new Promise(function (resolve, reject) {
        meteor.once('error', function () {
          reject();
        });
        meteor.once('exit', function () {
          resolve();
        });
        meteor.kill();
      })

    ]);
  };// kill

};

util.inherits(Transponder, EventEmiter);

// HELPERS

function uniqe() {
  if (!uniqe.counter) { uniqe.counter = 0; }
  return uniqe.counter++;
}



@@ -736,22 +736,42 @@
     var 
-n
+dbName = options.dbN
 ame 
-=
+%7C%7C
  'gagari
@@ -788,31 +788,73 @@
 .now();%0A
-%0A
       
-process.
+var env = Object.create(process.env);%0A      %0A      
 env.MONG
@@ -906,17 +906,19 @@
 + '/' + 
-n
+dbN
 ame;%0A   
@@ -916,32 +916,24 @@
 Name;%0A      
-process.
 env.ROOT_URL
@@ -933,17 +933,16 @@
 OOT_URL 
- 
 = 'http:
@@ -969,24 +969,16 @@
 ;%0A      
-process.
 env.PORT
@@ -978,21 +978,16 @@
 nv.PORT 
-     
 = port;%0A
@@ -1050,24 +1050,16 @@
  %7B env: 
-process.
 env %7D);%0A
@@ -1426,17 +1426,19 @@
 Server, 
-n
+dbN
 ame).the
@@ -1520,24 +1520,69 @@
        %7D%7D);%0A
+            gagarin.location = env.ROOT_URL;%0A
             

Allow dbName configuration