/**
 * XMLHTTP Request object
 *  - returns a XMLHTTP request object
 * @component AJAX
 */
function RequestObject()
{
	if(browser.isIE)
		return new ActiveXObject("Microsoft.XMLHTTP");
	else
		return new XMLHttpRequest();
}
/**
 * HTTP Request processor
 * - processes requests
 * @component AJAX
 */
function RequestProcessor()
{
	this.queue = new Array();
	this.position = 0;
	this.busy = false;
	this.async = true;
	this.showLoadingNotice = false; //option to show loading notices. NOTE: This may not work very reliably with simultanious requests
	
	// the async parameter is depreciated, please specify the parameter in the request object
	this.add = function(request,async)
	{
		// Use a timeout to put in new requests or strange errors will occur if the previous request was aborted, possibly because of some sort of
		// strange internal browser timing issue.
		var ref = this;
		setTimeout
		(
			function()
			{
				request.requestProcess = ref; //add reference to this to request obj
				ref.queue[ref.queue.length] = request;
				ref.process();
			},
			10
		);
		return this.queue.length;
	}
	this.abort = function(ticket)
	{
		if(this.position == ticket)
		{
			http.abort();
		}
		this.queue[ticket].abort = true;
	}
	
	this.abortCurrent = function()
	{
		http.abort();
		if(typeof(this.queue[this.position - 1]) != 'undefined')
		{
			this.queue[this.position - 1].abort = true;
		}
	}
	
	this.process = function()
	{
		if(this.busy||http.readyState!=0&&http.readyState!=4)
		{
			if(this.queue[this.position+1])
			{
				setTimeout(function(){requestProcess.process();},50);
			}
			return;
		}
		this.busy = true;
		
		//draw loading message if we are showing loading notices.
		if(this.showLoadingNotice) notifier.loading(notifier.LOADING_TRUE);
		
		if(!this.queue[this.position])
		{
			this.position--;
		}
		var current = this.queue[this.position];
		this.position++;
		if(current.abort)
		{
			this.done();
			return;
		}
		
		http.open(current.method, current.address, current.async);
		if(current.method == 'POST')
		{
			
			http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=utf-8');
			http.setRequestHeader('Content-length', current.data.length);
		}
		
		//NOTE: for an SYNCHRONOUS request, this shouldnt really be called, though IE and Firebug makes Firefox call it.
		http.onreadystatechange = current.onStateChange;

		http.send(current.data);
		
		//if a SYNCHRONOUS request, then we dont need to use busy, as execution will be halted on http.send() until a response is received.
		if(this.async == false)
		{
			//enable this line if you want to use callbacks with SYNCHRONOUS requests..
			//current.onStateChange();
			//this.busy = false;
		}
	}
	this.done = function()
	{
		this.busy = false;
		if(this.position < this.queue.length)
		{
			setTimeout(function(){requestProcess.process();},50);
		}
	}
}
/**
 * Request object
 * - holds all information for a request
 * @component AJAX
 */
function Request(method, address, data, onStateChange, onFail, output)
{
	var ref = this;
	
	this.async = true;
	this.method = method;
	this.address = address;
	this.data = data;

	this.requestProcess = false; //to reference RequestProcess object that is handling this request
	this.output = output;

	//this may not be called in synchronous requests...
	this.onStateChange = function()
	{
		if(http.readyState == 4)
		{
			//HIDE loading message if we are showing loading notices.
			if(ref.requestProcess && ref.requestProcess.showLoadingNotice)
			{
				notifier.loading(notifier.LOADING_FALSE);
			}
			
			try
			{
				if(http.status != 200)
				{
					throw http.status;
				}
			}
			catch(e)
			{
				// If the request was aborted, then we will get a value of 0 here in IE, and a long and detailed error string containing NS_ERROR_NOT_AVAILABLE
				// in Firefox.
				if(e != 0 && !e.toString().match('NS_ERROR_NOT_AVAILABLE'))
				{
					switch(e)
					{
						case 404:
						{
							notifier.notify('error','404 Not found, please try again');
							break;
						}
						case 500:
						{
							notifier.notify('error','500 Internal error, please try again');
						}
						default:
						{
							notifier.notify('error','Communication error, please try again');
						}
					}
				}
				onFail();
				requestProcess.done();
				return;
			}
			
			if(ref.output==true)
			{
				onStateChange(http.responseText);
			}
			else
			{
				try
				{
					var response = eval('('+http.responseText+')');
				}
				catch(e)
				{
					notifier.notify('error','Unable to interpret server response');
					onFail();
					requestProcess.done();
					return;
				}
				
				try
				{
					switch(response['status'])
					{
						case 'ok':
						{
							onStateChange(response['data']);
							break;
						}
						case 'info':
						{
							notifier.notify('info', response['info']);
							onStateChange(response['data']);
							break;
						}
						case 'warning':
						{
							notifier.notify('warning', response['warning']);
							onStateChange(response['data']);
							break;
						}
						case 'error':
						{
							notifier.notify('error', response['error']);
							onFail(response['error']);
							break;
						}
						case 'kick':
						{
							createCookie('error', response['error'], 0);
							window.location = 'index.php?logout=1';
							break;
						}
						default:
						{
							notifier.notify('error', 'Garbage? '+http.responseText);
							break;
						}
					}
				}
				catch(e)
				{
					notifier.notify('error',e.message);
					//alert('Request.onStateChange():: '+e.message);
				}
			}
			requestProcess.done();
		}
	}
	this.abort = false;
}
