var util = require('util');
var events = require('events');

function Promise(done) {
  Promise.super_.call(this);

  this.results = null;
  this.errors  = null;
  this.ended   = false;

  // Done Callback
  if ('function' === typeof done) {
    this.done(done);
  }
}
util.inherits(Promise, events.EventEmitter);
Promise.prototype.resolve = function() {
  // Arguments processing
  var args = slice(arguments);

  // Done
  this.emit('resolve', args);
  this.ended   = true;
  this.results = args;

  return this;
};
Promise.prototype.reject = function() {
  // Arguments processing
  var args = slice(arguments);

  // Error!
  this.emit('reject', args);
  this.ended  = true;
  this.errors = args;

  return this;
};
Promise.prototype.then = function(onResolve, onReject) {
  var promise = new Promise();
  var self = this;

  onResolve = onResolve || noop;
  onReject  = onReject  || noop;

  self
    .done(function() {
      var ret = onResolve.apply(self, arguments);

      if (ret instanceof Promise) {
        ret.then(
          function() {
            promise.resolve.apply(promise, arguments);
          },
          function(err) {
            promise.reject(err);
          }
        );
      } else if (ret instanceof Error) {
        promise.reject(ret);
      }
    })
    .fail(function(err) {
      onReject.call(self, err);

      promise.reject(err);
    });

  return promise;
};
Promise.prototype.done = function(callback) {
  var self = this;

  if (self.ended) {
    // Done before
    if (self.results !== null) {
      callback.apply(self, self.results);
    }
  } else {
    // Event listening
    self.on('resolve', function(args) {
      var ret = callback.apply(self, args);

      if (ret instanceof Promise) {
        ret.fail(self.reject.bind(self));
      }
    });
  }

  return self;
};
Promise.prototype.fail = function(callback) {
  var self = this;

  if (self.ended) {
    // Reject Before
    if (self.errors !== null) {
      callback.apply(self, self.errors);
    }
  } else {
    // Event listening
    self.on('reject', function(args) {
      callback.apply(self, args);
    });
  }

  return self;
};
function slice(argv) {
  var args = [];

  for (var i = 0; i < argv.length; i++) {
    args[i] = argv[i];
  }

  return args;
}
function noop() {
  return false;
}