var EventEmitter = require('events').EventEmitter;
var inherits = require('util').inherits;

var XMLHttpRequestMock = require('./lib/XMLHttpRequest/');
var XDomainRequestMock = require('./lib/XDomainRequest/');
var native = require('./lib/native');
var support = require('./lib/support');

function FauxJax() {
  this._installed = false;
}

inherits(FauxJax, EventEmitter);

FauxJax.prototype.install = function() {
  if (this._installed) {
    this.emit('error', new Error('faux-jax: Cannot call `install()` twice. Did you forgot to call `restore()`?'));
    return;
  }

  this._installed = true;

  // only modify the writable state of XMLHttpRequest in old ies when installing
  // it will be done only once
  require('./lib/make-native-implementations-writable')();

  if (this.support.xhr) {
    global.XMLHttpRequest = FakeXHR;
  }

  if (this.support.xdr) {
    global.XDomainRequest = FakeXDR;
  }
};

FauxJax.prototype.restore = function() {
  if (!this._installed) {
    this.emit('error', new Error('faux-jax: Cannot call `restore()` when not installed'));
    return;
  }

  if (support.xhr) {
    global.XMLHttpRequest = native.XMLHttpRequest;
  }

  if (support.xdr) {
    global.XDomainRequest = native.XDomainRequest;
  }

  this.removeAllListeners('request');
  this._installed = false;
};

FauxJax.prototype.waitFor = function(n, callback) {
  var fj = this;
  var fakeRequests = [];

  this.on('request', waitFor);

  function waitFor(fakeRequest) {
    fakeRequests.push(fakeRequest);
    if (fakeRequests.length === n) {
      fj.removeListener('request', waitFor);
      callback(null, fakeRequests);
    }
  }
};

FauxJax.prototype._newRequest = function(fakeRequest) {
  if (this.listeners('request').length === 0) {
    this.emit('error', new Error('faux-jax: received an unexpected request: ' + fakeRequest.requestURL));
    return;
  }

  this.emit('request', fakeRequest);
};

FauxJax.prototype.support = support;

var fauxJax = new FauxJax();

function FakeXHR() {
  var x = this;
  XMLHttpRequestMock.call(this);
  process.nextTick(function() {
    fauxJax._newRequest(x);
  });
}

inherits(FakeXHR, XMLHttpRequestMock);

function FakeXDR() {
  var x = this;
  XDomainRequestMock.call(this);
  process.nextTick(function() {
    fauxJax._newRequest(x);
  });
}

inherits(FakeXDR, XDomainRequestMock);

module.exports = fauxJax;

@@ -1984,31 +1984,147 @@
 FakeXHR() %7B%0A
+  XMLHttpRequestMock.call(this);%0A%7D%0A%0Ainherits(FakeXHR, XMLHttpRequestMock);%0A%0AFakeXHR.prototype.send = function() %7B%0A
   var 
-x
+req
  = this;%0A  X
@@ -2145,38 +2145,79 @@
 ock.
-call(this);%0A  process.nextTick
+prototype.send.apply(req, arguments);%0A  if (req.async) %7B%0A    setTimeout
 (fun
@@ -2218,32 +2218,34 @@
 ut(function() %7B%0A
+  
     fauxJax._new
@@ -2256,58 +2256,68 @@
 est(
-x
+req
 );%0A
+  
   %7D);%0A
-%7D%0A%0Ainherits(FakeXHR, XMLHttpRequestMock)
+  %7D else %7B%0A    fauxJax._newRequest(req);%0A  %7D%0A%7D
 ;%0A%0Af
@@ -2340,24 +2340,8 @@
 ) %7B%0A
-  var x = this;%0A
   XD
@@ -2373,26 +2373,178 @@
 s);%0A
-  process.nextTick
+%7D%0A%0Ainherits(FakeXDR, XDomainRequestMock);%0A%0AFakeXDR.prototype.send = function() %7B%0A  var req = this;%0A  XDomainRequestMock.prototype.send.apply(req, arguments);%0A  setTimeout
 (fun
@@ -2577,17 +2577,19 @@
 Request(
-x
+req
 );%0A  %7D);
@@ -2594,47 +2594,8 @@
 );%0A%7D
-%0A%0Ainherits(FakeXDR, XDomainRequestMock)
 ;%0A%0Am

add xhr sync support