/*
 * ajax_manager.js
 *
 * Copyright (C) 2006 - OS3 srl - http://www.os3.it
 *
 * Written by: Fabio Rotondo - fabio.rotondo@os3.it
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation;
 * version 2 of the License ONLY.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this software; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * NOTE: this is the GPL version of the library. If you want to include this 
 *       library inside a CLOSED SOURCE / PROPRIETARY software, you need 
 *       to purchase a COMMERCIAL LICENSE. See http://www.os3.it/license/
 *       for more info.
 *
 *       2009-05-04:	ADD: new static function AJAXManager.handle_easy
 *
 *       2009-01-24:	ADD: this.cbacks for:
 *
 *       			- serialize	serialize data
 *       			- req-start	request start
 *       			- req-end	request end
 *       			- req-error	request error
 *
 *       		ENH: AJAX Manager is now more "liwe"ized
 *       		ENH: removed String and Array enhancement dependencies
 *
 *       		DEP: error_handler has been DEPRECATED
 *       		DEP: start_req_handler has been DEPRECATED
 *       		DEP: end_req_handler has been DEPRECATED
 *
 */

// PUBLIC: ajax_response

// PUBLIC: AJAXManager
// PUBLIC  easy
// PUBLIC: request
function AJAXManager ()
{
	this._reqs = [];
	this._in_list = 0;
	this._in_abort = false;

	this.url = liwe.ajax_url;

	this.cbacks = {	"serialize" : null, 
			"req-start" : null, 
			"req-end" : null, 
			"req-error" : null 
		      };

	// {{{ request ( url, vars, callback, easy, sync )
	this.request = function ( url, vars, callback, easy, sync ) 
	{
		var req = null;
		var id = '';
		var t;
		var res = '';
		var self = this;
		var s;

		if ( ! url ) url = this.url;

		if ( vars ) 
		{
			for ( t in vars ) 
			{
				if ( typeof ( vars [ t ] ) == 'undefined' ) continue;
				if ( typeof ( vars [ t ] ) == 'function' ) continue;
				if ( ( typeof ( vars [ t ] ) == 'object' ) && ( vars [ t ] == null ) ) continue;

				/*
					This is a hack for IE, since it considers functions as objects (!!!)
				*/
				if ( typeof ( vars [ t ] ) == 'object' )
				{
					s = vars [ t ].toString ();
					if ( s.match ( /^function/ ) ) continue;
				}

				if ( vars [ t ] == '__arr' ) continue;

				if ( ( typeof ( vars [ t ] ) == 'string' ) || ( typeof ( vars [ t ] ) == 'number' ) )
				{
					res += t + "=" + this._ajax_escape ( vars [ t ] ) + "&";
				} else {
					try 
					{
						res += t + "=" + this._ajax_escape ( vars [ t ].toJSONString() ) + "&";
					} catch ( e ) {
						res += t + "=" + this._ajax_escape ( vars [ t ] ) + "&";
					}
				}
			}

			res = res.substr ( 0, res.length - 1 ); //  + "&";
		}

		req = this._build_req_obj ();


		// The request callback
		var _obj = this;
		if ( this.error_handler )
		{
			console.warn ( "AJAXManager.error_handler is DEPRECATED. Use cbacks [ 'req-error' ] instead." );
			req.onreadystatechange = function () { _obj._req_change ( req, callback, easy, _obj.error_handler ); };
		} else
			req.onreadystatechange = function () { _obj._req_change ( req, callback, easy, _obj.cbacks [ 'req-error' ] ); };

		var async = true;
		if ( sync ) async = false;

		// if (erroneously) the url starts with two "//", Firefox throws a security error
		url = url.replace ( /^\/\//g, "/" );

		req.open ( "POST", url, async );
		req.setRequestHeader ( 'Content-Type', 'application/x-www-form-urlencoded' );
		req.send ( res );

		if ( easy )
		{
			if ( this.start_req_handler ) 
			{
				console.warn ( "AJAXManager.start_req_handler is DEPRECATED. Use cbacks [ 'req-start' ] instead." );
				this.start_req_handler ( req );
			}

			if ( this.cbacks [ 'req-start' ] ) this.cbacks [ 'req-start' ] ( req );
		}
	};
	// }}}
	// {{{ easy ( arr, cback )
	this.easy    = function ( arr, cback ) { this.request ( null, arr, cback, true ); };
	// }}}
	// {{{ _build_req_obj ()
	this._build_req_obj = function ()
	{
		var req;

		/*@cc_on @*/
		/*@if (@_jscript_version >= 5)
		try {
			req = new ActiveXObject("Msxml2.XMLHTTP");
		} catch (e) {
			try {
				req = new ActiveXObject("Microsoft.XMLHTTP");
			} catch ( E ) {
				req = false;
			}
		}
		@end @*/

		if ( ! req && typeof XMLHttpRequest != 'undefined' ) 
		{
			try {
				req = new XMLHttpRequest();
			} catch (e) {
				req=false;
			}
		}

		if ( ! req && window.createRequest ) 
		{
			try {
				req = window.createRequest();
			} catch (e) {
				req=false;
			}
		}

		/*
		if ( window.XMLHttpRequest )		// Mozilla, Safari, Konqueror, Netscape...
		{
			req = new XMLHttpRequest ();
		} else {
			try {
				req = new ActiveXObject("Msxml2.XMLHTTP");
			} catch(e) {
				try {
					req = new ActiveXObject("Microsoft.XMLHTTP");
				} catch ( e ) {
					req = null;
					console.error ( "XMLHTTP Request object not available." );
				}
			}
		}

		*/

		this._reqs.push ( req );

		return req;
	};
	// }}}
	// {{{ _req_change ( req, callback, easy, err_handler )
	this._req_change = function ( req, callback, easy, err_handler )
	{
		if ( ! easy ) 
		{
			if ( callback ) callback ( req );
		} else {
			var in_abort = this._in_abort;

			// Remove the req from the Request Pool
			if ( req.readyState == 4 ) 
			{
				if ( this.end_req_handler ) 
				{
					console.warn ( "AJAXManager.end_req_handler is DEPRECATED. Use cbacks [ 'req-end' ] instead." );
					this.end_req_handler ( req );
				}

				if ( this.cbacks [ 'req-end' ] ) this.cbacks [ 'req-end' ] ( req );


				this._remove_req ( req );

				if ( in_abort ) return;

				AJAXManager.handle_easy ( req.responseText, callback, err_handler );
			}
		}
	};
	// }}}

	

	this.error_handler = null;		// DEPRECATED
	this.start_req_handler = null;		// DEPRECATED
	this.end_req_handler = null;		// DEPRECATED

	// {{{ _remove_req ( req )
	this._remove_req = function ( req )
	{
		var t, l = this._reqs.length;

		if ( this._in_list ) 
		{
			var obj = this;

			setTimeout ( function () { obj._remove_req ( req ); }, 100 );
			return;
		}

		this._in_list = 1;

		for ( t = 0; t < l; t ++ )
		{
			if ( this._reqs [ t ] == req ) break;
		}
		if ( t < l )
			this._reqs.splice ( t, 1 );

		this._in_list = 0;
	};
	// }}}
	// {{{ abort ( cback )
	this.abort = function ( cback )
	{
		var t, l = this._reqs.length;

		if ( this._in_list ) 
		{
			var obj = this;

			//console.debug ( "In list: " + this._in_list );
			setTimeout ( function () { obj.abort (); }, 100 );
			return;
		}

		this._in_list = 2;
		this._in_abort = true;

		for ( t = 0; t < l; t ++ )
		{
			try
			{
				this._reqs [ t ].abort ();
			} catch ( e ) {
			}
		}

		this._in_abort = false;
		this._in_list = 0;

		if ( cback ) cback ();
	};
	// }}}
	
	this._ajax_escape = function ( s )
	{
		if ( this.cbacks [ 'serialize' ] )
			s = this.cbacks [ 'serialize' ] ( s );

		s = escape ( s );
		s = s.replace ( /\+/g, "%2B" );
		return s;
	};
}

AJAXManager.handle_easy = function ( responseText, callback, err_handler )
{
	var resp_txt = responseText.replace ( /^\s+/, "" );

	if ( resp_txt.substr ( 0, "var ajax".length ) == 'var ajax' )
	{
		try {
			eval ( resp_txt );
			if ( ajax_response [ 'err_code' ] )
			{
				if ( ! err_handler )
				{
					if ( ajax_response [ 'err_descr' ] )
					{
						console.error ( ajax_response [ 'err_descr' ] );
						alert ( ajax_response [ 'err_descr' ] );
					} else {
						console.error ( "Generic Request error. Error code: " + ajax_response [ 'err_code' ] );
					}
				} else
					err_handler ( ajax_response );

				return;
			}
		} catch ( e ) {
			console.error ( "ERROR IN EVAL: %s - (except: %o)", responseText, e );
			return;
		}

		if ( callback && typeof ( callback ) == 'function' ) callback ( ajax_response );
	} else
		console.error ( "Request ERROR: " + responseText );
};

liwe.AJAX = new AJAXManager ();

liwe.AJAX._multi = {};
liwe.AJAX._multi_data = {};

liwe.AJAX.add = function ( name, action, dict, cback )
{
	var multi;
	var data;

	if ( ! name   ) name = "AJAX";
	if ( ! action ) action = null;


	multi = this._multi [ name ];
	if ( ! multi ) 
	{
		multi = [];
		data  = {};
	} else {
		data = this._multi_data [ name ];
	}

	if ( data [ 'running' ] ) 
	{
		console.error ( "Requests are already running for: %s", name );
		return;
	}

	multi.push ( [ action, dict, cback ] );
	this._multi [ name ] = multi;
	this._multi_data [ name ] = data;

	console.debug ( "Multi: %s - Len: %d", name, multi.length );
};

liwe.AJAX.start = function ( name, cback )
{
	var multi, data, t, l, req, func;

	multi = this._multi [ name ];
	if ( ! multi ) 
	{
		console.error ( "There is no multi group called: %s", name );
		return;
	}

	data = this._multi_data [ name ];
	data [ 'running' ] = true;
	data [ 'len' ] = multi.length;
	data [ 'count' ] = 0;

	l = multi.length;
	var i;
	for ( t = 0; t < l; t ++ )
	{
		req = multi [ t ];

		liwe.AJAX._send ( name, req, cback );
	}
};

liwe.AJAX._send = function ( name, req, cback ) 
{
	this.request ( req [ 0 ], req [ 1 ], function ( v ) { liwe.AJAX._req_cback ( name, v, req, cback ); }, true );
};

liwe.AJAX._req_cback = function ( name, v, req, cback )
{
	var multi = liwe.AJAX._multi [ name ];
	var data = liwe.AJAX._multi_data [ name ];

	if ( req [ 2 ] ) req [ 2 ] ( v );
	

	data [ 'count' ] += 1;

	if ( data [ 'count' ] == data [ 'len' ] ) 
	{
		if ( cback ) cback ();
		liwe.AJAX._multi [ name ] = null;
		liwe.AJAX._multi_data [ name ] = null;
	}
};
