/**
 * AJAX gateway class
 *
 * This object should be used to exchange data asychronously 
 *
 *
 * @author Ollie Maitland <ollie@byng-systems.com>
 * @copyright Byng Systems LLP
 * @requires Mootools-1.2
 */

/**
 * Represents a receieved response
 * 
 * @type Integer
 */
var BYNG_AJAX_STATE_RECEIVED = 4;


Byng.register('byng.ajax',
{
	/**
	 * Implement Options and Events
	 * 
	 */
	Implements: [Events, Options],
	
	/**
	 * Holds the default options for ByngAjax
	 * 
	 * @type Object
	 */
	options : {
		
		/**
		 * Method of AJAX call
		 * 
		 * @type String 
		 */
		method : 'POST',
		
		/**
		 * URL of AJAX call
		 * 
		 * @type String
		 */
		gateway : null,
		
		/**
		 * Holds the variables for the request
		 * 
		 * @type Hash
		 */
		vars : new Hash()		
	},
	
	/**
	 * Construct the ByngAjax dispatcher
	 * 
	 * @param Object options
	 * @param Function onResponse Response handler
	 * @param String method Method of request (GET or POST)
	 */
	initialize : function (options, onResponse, method) 
	{
		if ($type(options) == 'string') {
			options = {	'gateway' 	 : options, 
						'onResponse' : onResponse, 
						'method' 	 : method };
		}
		
		if ($type(options) == 'object') {
			this.options = $merge(this.options, options);
			this.fromRequest(options.request);
			if ($type(this.options.onResponse) == 'function') {
				this.addEvent('response', this.options.onResponse);
			}
			if ($type(this.options.onSuccess) == 'function') {
				this.addEvent('success', this.options.onSuccess);
			}
			this.options._gateway = this.options.gateway;	
		}	
		
	},
	
	/**
	 * Get a transport dispatcher
	 * 
	 * @return XMLHttpRequest
	 */
	getTransport : function ()
	{
		// Our transport method
		if (!this.xmlhttp) {
			this.xmlhttp = this.newTransport();
			// Set-up the state change handler
			this.xmlhttp.onreadystatechange = function () { 
				this.stateDispatch();
			}.bind(this);
		}
		return this.xmlhttp;
	},
	
	/**
	 * Create a new transport dispatcher
	 * 
	 * @return XMLHttpRequest
	 */
	newTransport : function ()
	{
		var xmlhttp = false;
		/*@cc_on @*/
		/*@if (@_jscript_version >= 5)
		// JScript gives us Conditional compilation, we can cope with old IE versions.
		// and security blocked creation of the objects.
		 try {
		  xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
		 } catch (e) {
		  try {
		   xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
		  } catch (E) {
		   xmlhttp = false;
		  }
		 }
		@end @*/
		if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
			try {
				xmlhttp = new XMLHttpRequest();
			} catch (e) {
				xmlhttp=false;
			}
		}
		if (!xmlhttp && window.createRequest) {
			try {
				xmlhttp = window.createRequest();
			} catch (e) {
				xmlhttp=false;
			}
		}
				
		return xmlhttp;
	},	
	
	/**
	 * Add a new parameter to the request
	 * 
	 * @param String key
	 * @param String value
	 */ 
	addParam : function (key, value) 
	{
		if (key == "action") return;
		if (value == null || !$chk(value)) return;
		this.options.vars.set(key, value);
	},
	
	/**
	 * Update the gateway with the added parameters
	 * 
	 * @return String
	 */ 
	composeQueryString : function () 
	{
		this.options.gateway = this.options._gateway;
		if (this.options.vars) {
			this.options.gateway = this.varsToQueryString(this.options.gateway);
		}
		
		return this.options.gateway;
	},
	
	/**
	 * Create query string for variables
	 * 
	 * @param String s
	 * @return String
	 */
	varsToQueryString : function (s)
	{
		if (!s) s = '';
		else s += "?";
		
		$each(this.options.vars, function(v,k) {
			
			var x = new String(v);
				x.replace(/\n/g, "<br />");
			
			s += encodeURIComponent(k) + '=' + encodeURIComponent(x) + '&';
			
		}.bind(this));
		
		return s;
	},
	
	/**
	 * Legacy method
	 * 
	 * @deprecated
	 * @return String
	 */
	setGateway : function ( gateway )
	{
		if (gateway) {
			this.setOptions({'gateway' : gateway, '_gateway' : gateway});
		}
		return this.composeQueryString();
	},
	
	/**
	 * Set htmlGet variables from a ByngRequest
	 * 
	 * @param ByngRequest request
	 */
	fromRequest : function ( request ) 
	{	
		if ($type(request) != 'object') return;
		
		// set the gateway
		this.resetGateway (request.composeAsString(true));
		
		// add the params in 
		for (k in request.vars) {
			this.addParam (k, request.vars[k]);
		}
		
		if (request.getAction() && this.options.method == 'POST') {
			this.setAction (request.getAction());
		}		
	},	
	

	/**
	 * Reset the gateway base
	 * 
	 * @param String gateway
	 */
	resetGateway : function (gateway)
	{
		this.options.gateway = gateway;
		this.options._gateway = gateway;
		this.options.vars = new Hash();
	},	
	
	/**
	 * Send a GET request with parameters
	 *
	 * @param Object query
	 * @return ByngAjax 
	 */ 
	get : function (query)
	{		
		// condition: query passed as an Array
		if( $chk(query) && typeof query != 'string' ) {
			// merge in variables
			$each(query, function(v,k) {
				this.options.vars.set(k,v);	
			}.bind(this));
		}

		this.getTransport().open("GET", this.composeQueryString(), true);
		
		this.setBusy( true );
		this.getTransport().send(null);
		
		return this;
	},
	
	/**
	 * Set a POST request
	 * 
	 * @param Object quert
	 */ 
	post : function () 
	{					
		// Send the request
		this.getTransport().open("POST", this.options.gateway, true);
		// Get the header to emulate a form submission  	
		this.getTransport().setRequestHeader('Content-type','application/x-www-form-urlencoded');
		this.setBusy( true );		
		this.getTransport().send(this.varsToQueryString());
		return this;
	},
	
	/**
	 * Send a request based on method
	 * 
	 * @param String query
	 * @return ByngAjax
	 */
	sendRequest : function (query) 
	{
		if (this.options.method == "POST") {
			return this.post (query);
		} else {
			return this.get (query);
		}
	},
	
	/**
	 * Handler a change of state
	 * 
	 */ 
	stateDispatch : function () 
	{
		if (this.xmlhttp == null) {
			this.fireEvent('cancel');
		} else if (this.xmlhttp.readyState == 4) {
			// finished
			this.setBusy( false );
			
			// fire the onComplete event
			if (this.options.cache) {
				this.options.cache.pool.set(this.options.cache.id,this.xmlhttp.responseText);
			}
			this.fireEvent('response',[this.xmlhttp.responseText]);
			this.xmlhttp = null;
		}
	},
	
	/**
	 * Check for an event on this AJAX request
	 * 
	 * @param Boolean
	 */
	hasEvent : function (type)
	{
        if ((this.$events) && (this.$events[type])) return true;
        else return false;
	},
	
	setBusy : function ( bool )
	{
		this.isBusy = bool;
	}
});
