var AjaxCallbacks = {
    showLoading: function(){},
    hideLoading: function(){}
}
var AjaxDispatcher = {
	busy: false,
	queue: new Array(0),
	addWaitingEvent: function (request){
		if (this.queue.length > 0){
			var lastRequestHash = $H(this.queue[this.queue.length-1]);
			var newRequestHash  = $H(request);
			if (lastRequestHash.toQueryString() == newRequestHash.toQueryString()) return;
		}
		this.queue.push(request);
	},
	executeWaitingEvent: function(){
		this.busy = false;
		if (this.queue.lenght == 0) return;
		var r = this.queue.shift();
		this.executedRequest = r.options;
		if (r.options.silent == undefined) r.options.silent = false;
		if (r.options.silent == false){
			hideError();
			hideStatus();
		}
		ajaxRequest(r.method, r.options);
	},
	setExecutedRequest: function(request){
		if (request.onError == undefined) request.onError = function(){};
		if (request.onComplete == undefined) request.onComplete = function(){};
		if (request.onSuccess == undefined) request.onSuccess = function(){};
		if (request.extraParam == undefined) request.extraParam = null;
		this.executedRequest = request;
	},
	executedRequest:null
}
function ajaxUpdate (element, method, options){
    if (options == undefined) options = {};
	options.updateElement = element;
	return ajaxRequest(method, options);
}

function ajaxFormSubmit(formId, onsuc){
	var data = Form.serialize( $(formId) );	
	ajaxRequest(null, {postBody: data, onSuccess: onsuc});	
	return false;
}
function ajaxRequest(method, options){
    if (options == undefined) options = {};
	if (AjaxDispatcher.busy) {
		return AjaxDispatcher.addWaitingEvent({method: method, options: options});
	}
	AjaxDispatcher.setExecutedRequest(options);
	AjaxDispatcher.busy = true;
	if (options == undefined) options = {};
	
	var protoOptions = {method:'post', onSuccess: ajaxOnSuccess, onFailure: ajaxOnFailure, postBody:options.postBody};
	
	if (method != null)
		protoOptions.postBody += '&action=' + method;
	if (options.onStart != undefined) options.onStart();
	if (options.silent != true)
		AjaxCallbacks.showLoading();
	hideError();
	new Ajax.Request('index.php?ajax', protoOptions);
}
function ajaxOnSuccess(transport){
	try{
		var response = transport.responseText.evalJSON();
	}catch(e){
		alert(e);
		return ajaxOnFailure({status:"handling", statusText:"invalid response" + t.responseText});
	}
	var isExec = {onSuccess:true, onError:false};
	var callParam = null;
	if (response.redirect != undefined){
		isExec.onSuccess = false;
		var loc =  response.redirect;
		if (loc.substr(0,8) == '::reload')
		    window.location.reload();
		else
		    window.location = loc;
	}
	if (response.error != undefined){
		isExec.onSuccess = false;
		showError(unescape(response.error));
		AjaxDispatcher.executedRequest.onError(unescape(response.error));
	}
	if (response.response != undefined){
	    if (typeof(response.response) == 'string'){
		    callParam = unescape(response.response);
    		if (AjaxDispatcher.executedRequest.updateElement != undefined){
    			$(AjaxDispatcher.executedRequest.updateElement).innerHTML = callParam;
    		}
		    try{
                callParam.evalScripts();
            }catch(e){
		        alert(e.message);
            }
		}else{
		    callParam = response.response;
		}		
	}
	if (response.status != undefined){
		showStatus(unescape(response.status));
	}
	if (isExec.onSuccess) AjaxDispatcher.executedRequest.onSuccess(callParam, AjaxDispatcher.executedRequest.extraParam);
	AjaxDispatcher.executedRequest.onComplete();
	
	AjaxCallbacks.hideLoading();
	AjaxDispatcher.executeWaitingEvent();
}
function ajaxOnFailure(t){
	alert('Error ' + t.status + ' -- ' + t.statusText);
	AjaxCallbacks.hideLoading();
	AjaxDispatcher.executeWaitingEvent();	
}
var statusMsgTimeoutID_e;
var statusMsgTimeoutID_s;
showError = function(message){
	if (!message) return;
	var reg=/errortip{(\w+)}(.+)/; 
	var chunks = message.split(';');
	var restMsg = [];
	for (var i=0; i < chunks.length; i++){
		var result=reg.exec(chunks[i]);
		if (result == null) {
			restMsg.push(chunks[i]);
			continue;
		}
		var elem = result[1];
		var message = result[2];
		try{
			new Insertion.After(elem, ' <span class="errorTip">' + message + '</span>');
			$(elem).addClassName('errorTip');
		}catch(e){
			restMsg.push(message);
		}
	}
	if (restMsg.length > 0){
		clearTimeout(statusMsgTimeoutID_e);
		$('error').innerHTML = message;
		Element.show('error');
		statusMsgTimeoutID_e = setTimeout('hideError();', 10000);
	}
}
showStatus = function(message){
	if (message == '') return;
	clearTimeout(statusMsgTimeoutID_s);
	$('status').innerHTML = message;
	Element.show('status');
	statusMsgTimeoutID_s = setTimeout('hideStatus();', 10000);
}
hideError = function(){
	var tips = $$('.errorTip');
	tips.each(
		function(s){
			if (s.tagName == 'SPAN'){
				s.remove();
			}else{
				s.removeClassName('errorTip');
			}
		}
	);
	$('error').innerHTML = '';
	Element.hide('error');	
}
hideStatus = function(){
	$('status').innerHTML = '';
	Element.hide('status');	
}