var Cathmhaol = window.Cathmhaol || {};

/**
* Creates an XHR object.
*
* @argument	{string} id	The ID to assign to the connection object
*
* @requires	Cathmhaol.Event	http://js.cathmhaol.com/cjl-event.js
* @requires	Cathmhaol.Request	http://js.cathmhaol.com/cjl-request.js
* @requires	Cathmhaol.Response	http://js.cathmhaol.com/cjl-response.js
*
* @author	Robert King (hrobertking@cathmhaol.com)
*
* @example	var o = new Cathmhaol.Ajax(); o.send(document.forms[0], new Array({name:'myvar', value:'yes', type:'hidden'}));
* @example	var o = new Cathmhaol.Ajax('connection1');
*/
Cathmhaol.Ajax = function(id) {
	/**
	* @property	Error raised
	* @type	{error}
	*/
	this.err = null;

	/**
	* @property	ID
	* @type	{string}
	*/
	this.id = id || Math.random().toString().replace(/0\./,"");

	/**
	* @property	Requests sent
	* @type	{Cathmahol.Request[]}
	*/
	this.requests = new Array();

	/**
	* @property	Responses received
	* @type	{Cathmhaol.Response[]}
	*/
	this.responses = new Array();

	/**
	* @property	Duration to wait for a response (in seconds)
	* @type	{float}
	*/
	this.timeout = null;

	/**
	* @method	Returns the specified request. If no request is specified, it returns the last request.
	* @returns	{Cathmhaol.Request}
	* @argument	{integer} index
	*/
	this.getRequest = function(index) {
		return me.requests[index || me.requests.length - 1];
	};

	/**
	* @method	Returns the specified request. If no request is specified, it returns the last request.
	* @returns	{Cathmhaol.Request}
	* @argument	{integer} index
	*/
	this.getResponse = function(index) {
		return me.responses[index || me.responses.length - 1];
	};

	/**
	* @method	Sends a request using AJAX. The resource argument can be a string representing the DOM node id or the DOM node (the element should be a URL, HTML form, or HTML anchor) to be used to generate the request or may be a Cathmhaol.Request object. Additional data (not present in the request) can be passed in an array of objects containing name, type, and value properties (like Input objects), e.g. new Array({name: 'foo', type:'hidden', value: 1}, {name: 'bar', type:'hidden', value: 2}); using the elements argument. Traditional request arguments, i.e. whether to send asynchronously and the username and password to present are available in the async, username, and password arguments respectively.
	* @returns	{void}
	* @argument	{node|string|Cathmhaol.Request} resource
	* @argument	{object[]} elements
	* @argument	{boolean} async
	* @argument	{string} username
	* @argument	{string} password
	*/
	this.send: function(resource, elements, async, username, password) {
		try {
			if (instanceof(resource) != Cathmhaol.Request) {
				resource = new Cathmhaol.Request(resource, elements, async, username, password);
			}
			if (_c && resource.form) {
				me.requests.push(resource);
				_s(Cathmhaol.Ajax.states.WAITING);
				_c.open(resource.form.method, resource.form.action, resource.async, resource.username, resource.password);
				_c.setRequestHeader("Cache-Control", "no-store, no-cache, must-revalidate");
				if (resource.form.method.toUpperCase() == "POST") {
					_c.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
					if (_c.overrideMimeType) { _c.overrideMimeType("text/xml"); }
				}
				_w(_c, me);
				_c.send(resource.data);
			}
		} catch (e) {
			_b(e);
			return;
		}
		return;
	};

	if (!Cathmhaol.Request || !Cathmhaol.Response || !Cathmhaol.Event) { return; }

	/**
	* @event	Fired when a script exception occurs
	*/
	this.exception = new Cathmhaol.Event();

	/**
	* @event	Fired when a the Cathmhaol.Response is ready and the HTTP status is not equal to 200
	*/
	this.error = new Cathmhaol.Event();

	/**
	* @event	Fired when the timeout expires or when a response (of any kind) is received
	*/
	this.finished = new Cathmhaol.Event();

	/**
	* @event	Fired when a response is received and a Cathmhaol.Response object is being created
	*/
	this.loading = new Cathmhaol.Event();

	/**
	* @event	Fired when the Cathmhaol.Response is ready and the HTTP status is equal to 200
	*/
	this.ready = new Cathmhaol.Event();

	/**
	* @event	Fired when the timeout expires
	*/
	this.timedout = new Cathmhaol.Event();

	/**
	* @event	Fired when a request is sent
	*/
	this.waiting = new Cathmhaol.Event();

	/**
	* @private
	* @method	Handle the script bomb.
	* @returns	{void}
	* @argument	{error} e
	*/
	function _b(e) {
		me.err = e;
		if (me.exception) { me.exception.trigger(); }
	}

	/**
	* @private
	* @method	Executed when the timeout expires.
	* @returns	{void}
	* @argument	{XmlHttpRequest} x
	*/
	function _e(x) {
		_s(Cathmhaol.Ajax.states.EXPIRED);
		return;
	}

	/**
	* @method	Destroys the timers
	* @returns	{void}
	*/
	function _k() {
		_t = window.clearInterval(_t);
		_x = window.clearTimeout(_x);
	};

	/**
	* @private
	* @method	Response handling.
	* @returns	{void}
	* @argument	{XmlHttpRequest} x
	*/
	function _p(x) {
		if (x) {
			switch (x.readyState) {
				case Cathmhaol.Ajax.readyStates.REQUEST_NOT_SENT:
					break;
				case Cathmhaol.Ajax.readyStates.OPENED:
					break;
				case Cathmhaol.Ajax.readyStates.RESPONSE_HEADERS_RECEIVED:
					break;
				case Cathmhaol.Ajax.readyStates.RESPONSE_LOADING:
					_s(Cathmhaol.Ajax.states.LOADING);
					break;
				case Cathmhaol.Ajax.readyStates.RESPONSE_DONE:
					try {
						var r = new Cathmhaol.Response(x);
						me.response.push(r);
						_s(r.status != Cathmhaol.Ajax.status.OK ? Cathmhaol.Ajax.states.HTTP_ERROR : Cathmhaol.Ajax.states.READY);
					} catch (e) {
						_b(e);
					}
					break;
			}
		}
		return;
	}

	/**
	* @private
	* @method	If the state attempts to change to an unrecognized state, the change is disallowed.
	* @returns	{void}
	* @argument	{Cathmhaol.Ajax.states} s
	*/
	function _s(s) {
		if (_m == Cathmhaol.Ajax.states.UNAVAILABLE && s != Cathmhaol.Ajax.states.INITIALIZED) { return; }
		switch (s) {
			case Cathmhaol.Ajax.states.EXPIRED:
				_m = s;
				if (me.timedout) { me.timedout.trigger(); }
				if (me.finished) { me.finished.trigger(); }
				break;
			case Cathmhaol.Ajax.states.HTTP_ERROR:
				_m = s;
				if (me.error) { me.error.trigger(); }
				if (me.finished) { me.finished.trigger(); }
				break;
			case Cathmhaol.Ajax.states.INITIALIZED:
				_m = s;
				break;
			case Cathmhaol.Ajax.states.LOADING:
				_m = s;
				if (me.loading) { me.loading.trigger(); }
				break;
			case Cathmhaol.Ajax.states.READY:
				_m = s;
				if (me.ready) { me.ready.trigger(); }
				if (me.finished) { me.finished.trigger(); }
				break;
			case Cathmhaol.Ajax.states.UNAVAILABLE:
				_m = s;
				break;
			case Cathmhaol.Ajax.states.WAITING:
				_m = s;
				if (me.waiting) { me.waiting.trigger(); }
				break;
			default:
				break;
		}
		return;
	}

	/**
	* @private
	* @method	Creates a timer that watches the XmlHttpRequest object every 20 milliseconds and optionally, a timeout with a duration set by the user.
	* @returns	{void}
	* @argument	{XmlHttpRequest} x
	*/
	function _w(x) {
		_t = window.setInterval(function() { _p.apply(s, [x]); }, 20);
		if (me.timeout) { _x = window.setTimeout(function() { _e.apply(s, [x]); }, me.timeout * 1000); }
		return;
	}


	/**
	* Constructor
	*
	* PRIVATE VARIABLES
	* _c	The XmlHttpRequest|MSXML object handling the request/response
	* _m	The current state
	* _t	The timer id of the watch interval
	* _x	The timer id of the timeout timer
	*/

	var me = this, _c, _m, _t, _x;
	try {
		var x, c, m;
		if (window.XMLHttpRequest) {
			_c = new XMLHttpRequest();
			_s(Cathmhaol.Ajax.states.INITIALIZED);
		} else if (typeof ActiveXObject != "undefined") {
			m = new Array("Microsoft.XMLHTTP","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP");
			for (c = 0; c < m.length; ++c) {
				try {
					_c = new ActiveXObject(m[c]);
					_s(Cathmhaol.Ajax.states.INITIALIZED);
					break;
				} catch (e) {
					_s(Cathmhaol.Ajax.states.UNAVAILABLE);
				}
			}
		}
	} catch (e) {
		_s(Cathmhaol.Ajax.states.UNAVAILABLE);
	}
};

/**
* Enumerated states for the Xhr object.
*
* @author	Robert King
* @version	1.0
*/
Cathmhaol.Ajax.states = {
	EXPIRED: -2,
	HTTP_ERROR: -4,
	INITIALIZED: 0,
	LOADING: 3,
	READY: 4,
	UNAVAILABLE: -8,
	WAITING: 1
};
/**
* Enumerated states for the XmlHttpRequest object.
*
* @author	Robert King
* @version	1.0
*/
Cathmhaol.Ajax.readyStates = {
	REQUEST_NOT_SENT: 0,
	OPENED: 1,
	RESPONSE_HEADERS_RECEIVED: 2,
	RESPONSE_LOADING: 3,
	RESPONSE_DONE: 4
};
/**
* Enumerated codes for HTTP status.
*
* @author	Robert King
* @version	1.0
*/
Cathmhaol.Ajax.status = {
	OK: 200
};

