/**
 * Expose `HttpContext`.
 */

module.exports = HttpContext;

/**
 * Module dependencies.
 */
 
var EventEmitter = require('events').EventEmitter
  , debug = require('debug')('http-context')
  , util = require('util')
  , inherits = util.inherits
  , assert = require('assert');
  
/**
 * Create a new `HttpContext` with the given `options`.
 *
 * @param {Object} options
 * @return {HttpContext}
 */

function HttpContext(resource, req, res, next) {
  EventEmitter.apply(this, arguments);
  
  this.resource = resource;
  this.req = req;
  this.res = res;
  this.next = next;
}

/**
 * Inherit from `EventEmitter`.
 */

inherits(HttpContext, EventEmitter);

/**
 * Override the default emitter behavior to support async or sync hooks before and after an event.
 */
 
HttpContext.prototype.emit = function (ev) {
  var ctx = this;
  var resource = this.resource;
  var origArgs = arguments;
  var args = Array.prototype.slice.call(arguments, 0)
  var success = arguments[arguments.length - 1];
  
  assert(typeof success === 'function', 'ctx.emit requires a callback');
  args.pop();
  
  var evName = ev;
  assert(typeof evName === 'string');
  args.shift();
  
  var listeners = resource.listeners(evName);
  var listener;
  
  // start
  next();
  
  function next(err) {
    if(err) return fail(err);

    try {
      if(listener = listeners.shift()) {
        var expectsCallback = listener._expects === args.length + 2;

        // if a listener expects all the `args`
        // plus ctx, and a callback
        if(expectsCallback) {
          // include ctx (this) and pass next to continue
          listener.apply(resource, args.concat([this, next]));
        } else {
          // dont include the callback
          listener.apply(resource, args.concat([this]));
          // call next directly
          next();
        }
      } else {
        success(done);
      }
    } catch(e) {
      fail(e);
    }
  }
  
  function fail(err) {
    ctx.done(err);
  }
  
  function done(err, result) {
    if(err) {
      return fail(err);
    }
    
    ctx.emit.apply(ctx, 
      ['after:' + evName] // after event
      .concat(args) // include original arguments/data
      .concat([function () { // success callback
        ctx.done.call(ctx, err, result);
      }])
    );
  };
}

HttpContext.prototype.done = function (err, result) {
  if(err) {
    this.next(err);
  } else {
    this.res.send(result);
  }
}