/**
 * Define an interface to make synchronous/asynchronous
 * requests to server.
 * TODO: Synchronous handling, blocking all other instances perhaps...
 */
// AJAX constants
var AJAX_DEBUG = 0;
var AJAX_ERROR = 3;
var AJAX_LOG = AJAX_DEBUG;
var AJAX_METHOD_POST = "POST";
var AJAX_METHOD_GET = "GET";
var AJAX_SYNCHRONOUS = false;
var AJAX_ASYNCHRONOUS = true;
var AJAX_NAMESPACE = "Ajax";
var AJAX_REQUEST_TAG = "Request";
var AJAX_RESPONSE_TAG = "Response";
var AJAX_PING = "ping";
var AJAX_PING_ACTION = "/ping.php";
var AJAX_KEEPALIVE_SESSION = 1;
var AJAX_KEEPALIVE_SESSION_TIMEOUT = 60000;
var AJAX_RESPONSE_STATUS_OK = "OK";
var AJAX_RESPONSE_STATUS_KO = "KO";

var AJAX_DIV_MESSAGE = "messages";
// AJAX messages (i18n)
var AJAX_REQUEST_NOT_FOUND = "Ajax request '%s' not registered";
var AJAX_REGISTER_SUCCESS = "Ajax register request '%s' successfully";
var AJAX_REGISTER_FAILED = "Ajax register request '%s' failed";
var AJAX_UNREGISTER_SUCCESS = "Ajax unregister request '%s' successfully";
var AJAX_UNREGISTER_FAILED = "Ajax unregister request '%s' failed";
var AJAX_REQUEST_SUCCESS = "Ajax request '%s' successfully";
var AJAX_REQUEST_FAILED = "Ajax request '%s' failed, page not found";
var AJAX_REQUEST_BAD_REQUEST = "Ajax request '%s' failed, error in path";
var AJAX_REQUEST_UNHANDLED_STATUS = "Ajax request '%s' unhandled with status '%t'";
var AJAX_REQUEST_UNINITIALIZED = "Ajax request '%s' uninitialized";
var AJAX_REQUEST_LOADING = "Loading Ajax request '%s'";
var AJAX_REQUEST_LOADED = "Ajax request '%s' loaded";
var AJAX_REQUEST_INTERACTIVE = "Ajax request '%s' interactive";
var AJAX_SESSION_KEEPALIVE = "Ajax pong received";
var AJAX_SESSION_DEAD = "Ajax pong not received, server error";
var AJAX_XML_CREATE_ERROR = "Ajax unable to create xml document.";
var AJAX_REQUEST_ERROR = "Ajax unknown error";


/**
 * Manages all requests. Main class.
 */
var AjaxEngine = function(){
	this.debugPrefix = "AjaxEngine";
	this.name = "AjaxEngine";
	// Attributes:
	this.requests = new Array();
	this.requestInstances = new Array();
	this.activeRequests = 0;
	
	// Keep session alive.
	this.register( AJAX_PING, AJAX_PING_ACTION, AJAX_ASYNCHRONOUS, AJAX_METHOD_POST, this.pong, this.pong );
}

/**
 * Register your requests, and the simply send XMLs.
 * Sample:
 * 	ajaxEngine.register( AJAX_SAVE_FORM, "<xxx>.do?action=<yyy>",
 *						 AJAX_ASYNCHRONOUS, showResponse );
 */
AjaxEngine.prototype.register = function( _reqName, _action, _asynchronous, _method, _callback, _failback ){
	this.requests[_reqName] = new Array( _action, _asynchronous, _method, _callback, _failback );
	this.requestInstances[_reqName] = new Array();
	if(AJAX_DEBUG) this.debug( AJAX_REGISTER_SUCCESS.replace("%s", _reqName) );
}

/**
 * Request won't be accessible anymore. Kill all active instances.
 * Sample:
 *	ajaxEngine.unregister( AJAX_SAVE_FORM );
 */
AjaxEngine.prototype.unregister = function( _reqName ){
	this.requests[_reqName] = null;
	this.activeRequests -= this.requestInstances[_reqName].length;
	this.requestInstances[_reqName] = null;
	if(AJAX_DEBUG) this.debug( AJAX_UNREGISTER_SUCCESS.replace("%s", _reqName) );
}

/**
 * Search for a request registerd with the name '_reqName',
 * instantiate it and send the '_xml' of the operation.
 * Sample:
 *	ajaxEngine.send( AJAX_SAVE_FORM, _vars );
 */
AjaxEngine.prototype.send = function( _reqName, _vars ){
	var params = this.requests[_reqName];
	if( !params ){
		if(AJAX_DEBUG) this.debug( AJAX_REQUEST_NOT_FOUND.replace("%s",_reqName) );
		return;
	}
	var _instance = _reqName + this.requestInstances[_reqName].length;
	var _request = new Request( this, _instance, _reqName,
							   params[0], params[1], params[2], params[3] );
	_request.create();
	_request.send( _vars );
	if( _request.asynchronous ){
		this.requestInstances[_reqName][_instance] = _request;
		this.activeRequests++;
	}
}

/**
 * Destroy a parsed instance.
 */
AjaxEngine.prototype.destroyInstance = function( _reqName, _instance ){
	this.requestInstances[_reqName][_instance] = null;
	this.activeRequests--;
}

/**
 * Genereic show messages function, to show passed message
 * into a DIV element called AJAX_DIV_MESSAGE in your document.
 */
AjaxEngine.prototype.showMessage = function( _message ){
	var _element = document.getElementById(AJAX_DIV_MESSAGE);
	if(_element) _element.innerHTML = _message;
}

/**
 * For debug purposes.

AjaxEngine.prototype.log = function( _message, level ){
	if(AJAX_LOG <= level && console && console.log) console.log(_message, level);
}

AjaxEngine.prototype.debug = function( _message ){
	this.log( _message, AJAX_DEBUG );
}

AjaxEngine.prototype.error = function( _message ){
	this.log( _message, AJAX_ERROR );
}*/

/**
 * Ping session keep alive!
 */
AjaxEngine.prototype.ping = function(){
	ajaxEngine.send( AJAX_PING, new Array() );
}

AjaxEngine.prototype.pong = function( _xmlResponse, _textResponse ){
	if( _xmlResponse && _xmlResponse.documentElement && _xmlResponse.documentElement.nodeName != "pong" )
		if(AJAX_DEBUG) this.debug( AJAX_SESSION_DEAD );
	else
		if(AJAX_DEBUG) this.debug( AJAX_SESSION_KEEPALIVE );
}

/**
 * Define request.
 */
var Request = function( _engine, _instance, _reqName, _action, _asynchronous, _method, _callback, _failback ){
	this.debugPrefix = "AjaxRequest";
	this.engine = _engine;
	this.instance = _instance;
	this.name = _reqName;
	this.method = (_method)?_method:AJAX_METHOD_POST;
	this.action = _action;
	this.req = null;
	this.asynchronous = _asynchronous;
	this.callback = _callback;
	this.failback = _failback;
}

/**
 * Create an http request, platform independemt.
 */
Request.prototype.create = function(){
	if (window.XMLHttpRequest) {
        this.req = new XMLHttpRequest();
    } else if (window.ActiveXObject) {
       	this.req = new ActiveXObject("Microsoft.XMLHTTP");
    } else {
    	this.req = null;
    	if(AJAX_DEBUG) this.debug( AJAX_XML_CREATE_ERROR );
    }
}

/**
 * Send the passed XML as body of this request.
 */
Request.prototype.send = function(  _vars ){
	var _Req = this;
	this.req.open(this.method, this.action, this.asynchronous);
	if( this.asynchronous ){
		this.req.onreadystatechange =
			function(){
				if(_Req.req.readyState == 0){ // Uninitialized
					if(AJAX_DEBUG) _Req.debugg( AJAX_REQUEST_UNINITIALIZED.replace("%s", _Req.name) );
				}
				else if(_Req.req.readyState == 1){ // Loading
					if(AJAX_DEBUG) _Req.debug( AJAX_REQUEST_LOADING.replace("%s", _Req.name) );
				}
				else if(_Req.req.readyState == 2){ // Loaded
					if(AJAX_DEBUG) _Req.debug( AJAX_REQUEST_LOADED.replace("%s", _Req.name) );
				}
				else if(_Req.req.readyState == 3){ // Interactive
					if(AJAX_DEBUG) _Req.debug( AJAX_REQUEST_INTERACTIVE.replace("%s", _Req.name) );
				}
				else if(_Req.req.readyState == 4){ // Complete
					try{
						if( _Req.req.status == 200 ){
							if(AJAX_DEBUG) _Req.debug( AJAX_REQUEST_SUCCESS.replace("%s", _Req.name) );
							_Req.callback( _Req.req.responseXML, _Req.req.responseText );
							_Req.engine.destroyInstance( _Req.name, _Req.instance );
						}
						else if( _Req.req.status == 404 ){
							if(AJAX_DEBUG) _Req.debug( AJAX_REQUEST_FAILED.replace("%s", _Req.name) );
							if( _Req.failback ) _Req.failback( _Req.req.responseXML, _Req.req.responseText );
						}
						else if( _Req.req.status == 400 ){
							if(AJAX_DEBUG) _Req.debug( AJAX_REQUEST_BAD_REQUEST.replace("%s", _Req.name) );
							if( _Req.failback ) _Req.failback( _Req.req.responseXML, _Req.req.responseText );
						}
						else{
							if(AJAX_DEBUG) _Req.debug( AJAX_REQUEST_UNHANDLED_STATUS.replace("%s", _Req.name).replace("%t", _Req.req.status) );
							if( _Req.failback ) _Req.failback( _Req.req.responseXML, _Req.req.responseText );
						}
					}catch(e){
						if(AJAX_DEBUG) _Req.error( AJAX_REQUEST_ERROR+"\n"+e );
					}
					_Req.engine.destroyInstance( _Req.name, _Req.instance );
				}
			};
	}
	this.req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	var vars = "";
	if( _vars ){
		for( _key in _vars )
			if( vars == "" )
				vars+=_key+"="+encodeURIComponent(_vars[_key]);
			else
				vars+="&"+_key+"="+encodeURIComponent(_vars[_key]);
	}
	this.req.setRequestHeader("Content-Length", vars.length);
	this.req.send( vars );
}

Request.prototype.showMessage = AjaxEngine.prototype.showMessage;
/*Request.prototype.log = AjaxEngine.prototype.log;
Request.prototype.debug = AjaxEngine.prototype.debug;
Request.prototype.error = AjaxEngine.prototype.error;*/

Request.prototype.XMLSerialize = function( _document ){
	var _xmlSerializer = new XMLSerializer();
    return _xmlSerializer.serializeToString(_document);
}

/**
* Super response status parsing.
*/
function responseStatus( _xmlResponse, _txtResponse ){
	if( _xmlResponse.documentElement.getAttribute("status") == AJAX_RESPONSE_STATUS_OK )
		return true;
	if( _xmlResponse.documentElement.getAttribute("status") == AJAX_RESPONSE_STATUS_KO )
		return false;
	return false;
}

var ajaxEngine = new AjaxEngine();
if( AJAX_KEEPALIVE_SESSION )
	setInterval( ajaxEngine.ping, AJAX_KEEPALIVE_SESSION_TIMEOUT );

