var defaultInterval = 2000;

function json_decode(str_json) {

	var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
	var j;
	var text = str_json;

	var walk = function(holder, key) {
        // The walk method is used to recursively walk the resulting structure so
        // that modifications can be made.
		var k, v, value = holder[key];
		if (value && typeof value === 'object') {
			for (k in value) {
				if (Object.hasOwnProperty.call(value, k)) {
					v = walk(value, k);
					if (v !== undefined) {
						value[k] = v;
					} else {
						delete value[k];
					}
				}
			}
		}
		return reviver.call(holder, key, value);
	}
	cx.lastIndex = 0;
	if (cx.test(text)) {
		text = text.replace(cx, function (a) {
			return '\\u' +
					('0000' + a.charCodeAt(0).toString(16)).slice(-4);
		});
	}

	if (/^[\],:{}\s]*$/.
	test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
	replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
			replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
				j = eval('(' + text + ')');
				return typeof reviver === 'function' ?
						walk({
					'': j
						}, '') : j;
			}
			throw new SyntaxError('json_decode');
}// }}}
function json_encode(mixed_val) {

	var indent;
	var value = mixed_val;
	var i;

	var quote = function (string) {
		var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
		var meta = {    // table of character substitutions
			'\b': '\\b',
	'\t': '\\t',
 '\n': '\\n',
 '\f': '\\f',
 '\r': '\\r',
 '"' : '\\"',
 '\\': '\\\\'
		};

		escapable.lastIndex = 0;
		return escapable.test(string) ?
				'"' + string.replace(escapable, function (a) {
			var c = meta[a];
            return typeof c === 'string' ? c :
						'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
	}) + '"' :
			'"' + string + '"';
}

    var str = function(key, holder) {
		 var gap = '';
		 var indent = '    ';
		 var i = 0;          // The loop counter.
		 var k = '';          // The member key.
		 var v = '';          // The member value.
		 var length = 0;
		 var mind = gap;
		 var partial = [];
		 var value = holder[key];

        // If the value has a toJSON method, call it to obtain a replacement value.
		 if (value && typeof value === 'object' &&
		 typeof value.toJSON === 'function') {
			 value = value.toJSON(key);
		 }

        // What happens next depends on the value's type.
		 switch (typeof value) {
			 case 'string':
					 return quote(value);

			 case 'number':
                // JSON numbers must be finite. Encode non-finite numbers as null.
					 return isFinite(value) ? String(value) : 'null';

			 case 'boolean':
			 case 'null':
                // If the value is a boolean or null, convert it to a string. Note:
                // typeof null does not produce 'null'. The case is included here in
                // the remote chance that this gets fixed someday.

					 return String(value);

			 case 'object':
                // If the type is 'object', we might be dealing with an object or an array or
                // null.
                // Due to a specification blunder in ECMAScript, typeof null is 'object',
                // so watch out for that case.
					 if (!value) {
				 return 'null';
					 }

                // Make an array to hold the partial results of stringifying this object value.
					 gap += indent;
					 partial = [];

                // Is the value an array?
					 if (Object.prototype.toString.apply(value) === '[object Array]') {
                    // The value is an array. Stringify every element. Use null as a placeholder
                    // for non-JSON values.

						 length = value.length;
						 for (i = 0; i < length; i += 1) {
							 partial[i] = str(i, value) || 'null';
						 }

                    // Join all of the elements together, separated with commas, and wrap them in
                    // brackets.
                    v = partial.length === 0 ? '[]' :
								  gap ? '[\n' + gap +
								  partial.join(',\n' + gap) + '\n' +
                    mind + ']' :
								  '[' + partial.join(',') + ']';
						  gap = mind;
						  return v;
					 }

                // Iterate through all of the keys in the object.
					 for (k in value) {
						 if (Object.hasOwnProperty.call(value, k)) {
							 v = str(k, value);
							 if (v) {
								 partial.push(quote(k) + (gap ? ': ' : ':') + v);
							 }
						 }
					 }

                // Join all of the member texts together, separated with commas,
                // and wrap them in braces.
                v = partial.length === 0 ? '{}' :
							 gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
							 mind + '}' : '{' + partial.join(',') + '}';
					 gap = mind;
					 return v;
		 }
	 };

	 return str('', {
		 '': value
	 });
}// }}}

function ajaxHandler( ) {}

ajaxHandler.prototype.queriesCounter = 0;
ajaxHandler.prototype.nonAutomaticMessages = new Array();
ajaxHandler.prototype.nonAutomaticMessagesCount = 0;

ajaxHandler.prototype.queriesToExecuteNextTurn = new Array();
ajaxHandler.prototype.registeredAutoqueryIntervalCounter = new Array();
ajaxHandler.prototype.registeredAutoqueries = new Array();

ajaxHandler.prototype.queryTimeoutCounter = 999;

var messagesToSendThisTurn = new Array();

/**
 * interval
 * sendFunction is a function that provides data to send (should return
 * recieveFunction defines a function that is executed on data recieve
 */
ajaxHandler.prototype.registerAutoquery = function ( interval, sendFunction, recieveFunction ) {
	this.registeredAutoqueryIntervalCounter[this.queriesCounter] = interval;
	this.registeredAutoqueries[this.queriesCounter] = new Array(interval, sendFunction, recieveFunction);
	this.queriesCounter++;
}

ajaxHandler.prototype.sendMessage = function () {

	/// check if we are not waiting for other query to come back
	if( this.queryTimeoutCounter++ < 5 ) { //set timeout for 5 intervals
		return true;
	}
	this.queryTimeoutCounter = 0; // reset timeout counter

	this.queriesToExecuteNextTurn = new Array();
	var x = 0;
	for(query in this.registeredAutoqueries) {
		//interval = this.registeredAutoqueryIntervalCounter[query];
		interval = this.registeredAutoqueries[query][0];
		this.registeredAutoqueryIntervalCounter[query]++;
		sendFunction = this.registeredAutoqueries[query][1];
		recieveFunction = this.registeredAutoqueries[query][2];
		dataToSend = eval(sendFunction+'()');
		this.queriesToExecuteNextTurn[x++] = new Array(interval, this.registeredAutoqueryIntervalCounter[query], dataToSend['module'], dataToSend['action'], dataToSend['paramArray'], recieveFunction);
	}

	y = -1;
	messagesToSendThisTurn = new Array();
	modulesSendingThisTurn = new Array();


	numberOfNonAutomaticMessages = this.nonAutomaticMessages.length;
	for( x=0; x<numberOfNonAutomaticMessages; x++ ) {
		y++;
		messageToSend = new Object();
		messageToSend['module'] = this.nonAutomaticMessages[x]['module'];
		messageToSend['action'] = this.nonAutomaticMessages[x]['action'];
		messageToSend['paramArray'] = this.nonAutomaticMessages[x]['paramArray'];
		messagesToSendThisTurn[y] = messageToSend;

		moduleSending = new Array();
		moduleSending[2] = this.nonAutomaticMessages[x]['module'];
		moduleSending[3] = this.nonAutomaticMessages[x]['action'];
		moduleSending[5] = this.nonAutomaticMessages[x]['callbackFunction'];
		modulesSendingThisTurn[y] = moduleSending;
	}



	for( x=0; x<this.queriesCounter; x++) {
		msg = this.queriesToExecuteNextTurn[x];
		if( msg[1] >= msg[0] ) {
			this.registeredAutoqueryIntervalCounter[x] = 0;
			y++;
		} else {
			this.registeredAutoqueryIntervalCounter[x]++ ;
			this.queryTimeoutCounter = 999;
			continue;
		}
		messageToSend = new Object();
		messageToSend['module'] = msg[2];
		messageToSend['action'] = msg[3];
		messageToSend['paramArray'] = msg[4];
		messagesToSendThisTurn[y] = messageToSend;
		modulesSendingThisTurn[y] = msg;
	}


	/// reset nonAutomaticMessages
	this.nonAutomaticMessagesCount = 0;
	this.nonAutomaticMessages = new Array();

	totalNumberOfMessagesToSendThisTurn = messagesToSendThisTurn.length;
	if( totalNumberOfMessagesToSendThisTurn > 0) {

		//alert(serialize(messagesToSendThisTurn));

		messageToSend = json_encode(messagesToSendThisTurn);
		messagesToSendThisTurn = new Array();

		$.ajax({
			type: "POST",
			url: "/ajax",
			data: 'msg='+messageToSend,
			success: function( msg ) {
				ah.queryTimeoutCounter = 999;
				arrayMessage = new Array();
				if( msg != '' ) {
					arrayMessage = json_decode( msg );
				}
				//alert( 'aa'+msg );
				for (z = 0; z < totalNumberOfMessagesToSendThisTurn; z++) {
					reg = modulesSendingThisTurn[z];
					jModule = reg[2];
					jAction = reg[3];
					if( arrayMessage[z] && arrayMessage[z][jModule] ) {
						eval(reg[5] + "(arrayMessage[z][jModule][jAction]);");
					}
				}
			}
		});
	} else {
		ah.queryTimeoutCounter = 999;
	}
}

ajaxHandler.prototype.addMessage = function(module, aaction, paramArray, callbackFunction) {
	messageToSend = new Object();
	messageToSend['module'] = module;
	messageToSend['action'] = aaction;
	messageToSend['paramArray'] = paramArray;
	messageToSend['callbackFunction'] = callbackFunction;
	this.nonAutomaticMessages[this.nonAutomaticMessagesCount++] = messageToSend;
}

ajaxHandler.prototype.start = function(objectName) {
	this.sendMessage();
	setTimeout(objectName+".start('"+objectName+"')",defaultInterval);
}

ajaxHandler.prototype.sendImmediately = function( module, aaction, paramArray, callbackFunction ) {
	messageToSend = new Object();
	messageToSend['module'] = module;
	messageToSend['action'] = aaction;
	messageToSend['paramArray'] = paramArray;

	arrayOfMessagesToSend = new Array();
	arrayOfMessagesToSend[0] = messageToSend;

	$.ajax({
		type: "POST",
		url: "/ajax",
		data: 'msg='+json_encode( arrayOfMessagesToSend ),
		success: function( msg ) {
			arrayMessage = new Array();
			if( msg != '' ) {
				arrayMessage = json_decode( msg );
			}
			//alert( 'aa'+msg );
			eval( callbackFunction + "(arrayMessage[0][module][aaction]);");

		}
	});
}


ah = new ajaxHandler( );
$( document ).ready( function( ) {
	ah.start('ah');
} );
