/*
 * Copyright © 20011, Roelof van Schalkwyk
 * 
 * All rights reserved.
 * 
 * Author: Roelof van Schalkwyk
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * @copyright  Copyright © 2011 Roelof van Schalkwyk
 */

//@todo convert this into an object?

var ajaxSyncRequests = new Array();

function loadAjaxDivs()
{
	if (debugAjax)
		$("body").append("<div id='debugAjax'></div>");
	
	$("body").append("<div id='ajaxMessage'></div>");
	$("#ajaxMessage").css({
		position: "absolute",
		top: 0,
		left: 0,
		width: "100%",
		height: "auto",
		backgroundColor: "#FFFFFF",
		zIndex: 100,
		textAlign: "center"
	});
}

function setStatusMessage(message,timeout)
{
	var messageDiv = $("#ajaxMessage");
	var html = "<span style='margin: 5px auto;'>"+message+"</span>";
	messageDiv.html(html);
	
	//@todo is there some jQuery way to do this?
	if (timeout != -1) {
		setTimeout(
				function () {
					$("#ajaxMessage").html("");
				}, timeout
			);
	}
}

//append an ajax loader div to a selector's elements
function appendAjaxLoader(selector)
{
	if ($(selector).children(".ajaxLoader").length > 0)
		return;
	
	var background = "url('"+uriBase+"images/ajax-load.gif') no-repeat center center white";
	
	$(selector).append("<div class='ajaxLoader'></div>");
	$(selector).children(".ajaxLoader").css({
		background: background,
		height: "30"
	});
}

//remove an ajax loader that has been appended to a selector's elements
function removeAjaxLoader(selector)
{
	$(selector).children(".ajaxLoader").remove();
}

//beforeSend functions

function keyIsAlnum(event)
{
	return isAlnum(String.fromCharCode(event.which));
}

//check whether the ajax request can be processed
//function checkAjaxRequest(xhr,settings,templateSelectors,async)
function checkAjaxRequest(xhr,settings,templateSelectors,lockSelectors)
{
	//@todo break this function up into several smaller ones to allow more flexibility in customisation
	
	//check whether the id is already being processed
	//@todo is there a better way to do this?
	var foundOne = false;
	for (var i=0;i<ajaxSyncRequests.length;i++) {
		//for (var j=0;j<templateSelectors.length;j++) {
		for (var j=0;j<lockSelectors.length;j++) {
			//if (ajaxSyncRequests[i] == templateSelectors[j]) {
			if (ajaxSyncRequests[i] == lockSelectors[j]) {
				foundOne = true;
				break;
			}
		}
		if (foundOne)
			break;
	}
	
	//since this is a synchronous request a new request on the id cannot be sent
	if (foundOne) {
		//@todo a debugging toggle for this message? or a timeout; some way to customise
		//$("#StatusMessage-Ajax").html("Your previous request is still being processed...");
		setStatusMessage("Your previous request is still being processed...",2500);
		return false;
	}
	
	/*//no current request exist on the id so add it to the current request array and allow the request to be sent
	ajaxSyncRequests = ajaxSyncRequests.concat(lockSelectors);
	
	for (var i=0;i<templateSelectors.length;i++)
		$("."+templateSelectors[i]).parent().css("cursor", "progress");
	
	for (var i=0;i<lockSelectors.length;i++) {
		var lockSelector = lockSelectors[i];
		var floatId = lockSelector+"-overlay";
		var container = $("."+lockSelector).parent();
		var pos = container.position();
  		var width = container.outerWidth();
  		var height = container.outerHeight();
  		container.fadeTo(0,0.4);
  		
		$("body").append("<div id='"+floatId+"'></div>");
		//var bgUrl = uriBase+"images/ajax-loader.gif"
		var background = "0";
		if (height > 30)
			background = "url('"+uriBase+"images/ajax-load.gif') no-repeat";
		$("#"+floatId).css({
  			position: "absolute",
			top: pos.top,
			left: pos.left,
			width: width,
			height: height,
			//background: "url('"+bgUrl+"') no-repeat #666666",
			background: background,
			backgroundPosition: "center center",
			zIndex: 100
  		});
		//$("#"+floatId).fadeTo(0,0.5);
	}
	
	//@todo a debugging toggle for this message? or a timeout; some way to customise
	//$("#StatusMessage-Ajax").html("Processing...");
	if (debugAjax)
		setStatusMessage("Processing...",-1);*/
	
	return true;
}

function addAjaxOverlay(container)
{
	var pos = container.position();
	var width = container.outerWidth();
	var height = container.outerHeight();
	
	container.fadeTo(0,0.4);
		
	container.append("<div class='overlay'></div>");
	var background = "0";
	if (height > 30)
		background = "url('"+uriBase+"images/ajax-load.gif') no-repeat center center";
	container.children(".overlay").css({
		position: "absolute",
		top: pos.top,
		left: pos.left,
		width: width,
		height: height,
		background: background,
		zIndex: 100
	});
}

function removeAjaxOverlay(container)
{
	container.fadeTo(0,1);
	container.children(".overlay").remove();
}

function initAjax(templateSelectors,lockSelectors)
{
	//no current request exist on the id so add it to the current request array and allow the request to be sent
	ajaxSyncRequests = ajaxSyncRequests.concat(lockSelectors);
	
	for (var i=0;i<templateSelectors.length;i++)
		$("."+templateSelectors[i]).parent().css("cursor", "progress");
	
	for (var i=0;i<lockSelectors.length;i++) {
		var lockSelector = lockSelectors[i];
		//var floatId = lockSelector+"-overlay";
		var container = $("."+lockSelector).parent();
		
		addAjaxOverlay(container);
		
		/*var pos = container.position();
  		var width = container.outerWidth();
  		var height = container.outerHeight();
  		container.fadeTo(0,0.4);
  		
		//$("body").append("<div id='"+floatId+"'></div>");
  		container.append("<div class='overlay'></div>");
		var background = "0";
		if (height > 30)
			background = "url('"+uriBase+"images/ajax-load.gif') no-repeat center center";
		//$("#"+floatId).css({
		container.children(".overlay").css({
  			position: "absolute",
			top: pos.top,
			left: pos.left,
			width: width,
			height: height,
			background: background,
			zIndex: 100
  		});*/
	}
	
	if (debugAjax)
		setStatusMessage("Processing...",-1);
}

//error funcs

//function ajaxError(xhr,status,error,templateSelectors,async)
function ajaxError(xhr,status,error,templateSelectors,lockSelectors)
{
	if (debugAjax)
		$("#debugAjax").html("<pre>"+htmlspecialchars(xhr.responseText)+"</pre>");
	
	for (var i=0;i<templateSelectors.length;i++)
		$("."+templateSelectors[i]).parent().css("cursor", "auto");
	
	for (var i=0;i<lockSelectors.length;i++) {
		var lockSelector = lockSelectors[i];
		var floatId = lockSelector+"-overlay";
		var container = $("."+lockSelector).parent();
		
		removeAjaxOverlay(container);
	}
	
	//@todo a debugging toggle for this message? or a timeout; some way to customise
	//$("#StatusMessage-Ajax").html("An error occurred while processing your request. Please refresh the page and try again.");

	//abort means the user refreshed the page, pushed the stop button (or abort was called by the script itself) so no message should be displayed
	if (status != "abort")
		setStatusMessage("An error occurred while processing your request. Please refresh the page and try again.",2500);
}

//success funcs

//function ajaxSuccess(text,status,xhr,templateSelectors,async)
function ajaxSuccess(text,status,xhr,templateSelectors,lockSelectors)
{
	if (debugAjax)
		$("#debugAjax").html("<pre>"+htmlspecialchars(xhr.responseText)+"</pre>");
	
	//need to do these here and not in ajaxComplete since the selector element may not exist after the reload
	for (var i=0;i<templateSelectors.length;i++)
		$("."+templateSelectors[i]).parent().css("cursor", "auto");
	
	for (var i=0;i<lockSelectors.length;i++) {
		var lockSelector = lockSelectors[i];
		var floatId = lockSelector+"-overlay";
		var container = $("."+lockSelector).parent();
		
		removeAjaxOverlay(container);
	}
	
	//parse the text to extract the right parts for each result block
	var startTag = "<ajax id=\"", endTag = "</ajax>", parseString = text, parseError = false, blockStart;
	
	//find the start index of the first block
	blockStart = parseString.indexOf(startTag);
	
	while (blockStart != -1 && !parseError) {
		var blockEnd, blockString, idEnd, id;
		
		//find the end index of the current block
		blockEnd = parseString.indexOf(endTag);
		
		if (blockEnd == -1) {
			parseError = true;
			break;
		}
		
		//extract the block id and content
		blockString = parseString.substring(blockStart,blockEnd);
		blockString = blockString.substring(startTag.length);
		idEnd = blockString.indexOf("\"");
		if (idEnd == -1) {
			parseError = true;
			break;
		}
		id = blockString.substring(0,idEnd);
		blockString = blockString.substring(idEnd+2);
		
		//load the content into the block
		$("."+id).parent().html(blockString);
		
		parseString = parseString.substring(blockEnd+endTag.length);
		blockStart = parseString.indexOf(startTag);
	}
	
	//parse the ajaxscript block
	startTag = "<ajaxscript>";
	endTag = "</ajaxscript>";
	parseString = text;
	parseError = false;
	
	//find the start index of the first block
	blockStart = parseString.indexOf(startTag);
	
	//find the end index of the current block
	var blockEnd = parseString.indexOf(endTag);
	
	if (blockEnd == -1)
		parseError = true;
	
	if (!parseError) {
		//extract the block id and content
		blockString = parseString.substring(blockStart + startTag.length,blockEnd);
		
		//load the content into the block
		eval(blockString);
	}
	
	if (parseError) {
		//@todo a debugging toggle for this message? or a timeout; some way to customise
		//$("#StatusMessage-Ajax").html("An error occurred while processing your request. Please refresh the page and try again.");
		setStatusMessage("An error occurred while processing your request. Please refresh the page and try again.",2500);
		return;
	}
	
	//@todo a debugging toggle for this message? or a timeout; some way to customise
	//$("#StatusMessage-Ajax").html("Request completed successfully.");
	if (debugAjax)
		setStatusMessage("Request completed successfully.",2500);
}

//complete funcs

//@todo implement toggling of debug display
//function ajaxComplete(xhr,status,templateSelectors,async)
function ajaxComplete(xhr,status,templateSelectors,lockSelectors)
{
	//$("#debugAjax").text(xhr.responseText);
	//$("#debugAjax").html("<pre>"+htmlspecialchars(xhr.responseText)+"</pre>");
	
	//if (async) {
		//for (var i=0;i<templateSelectors.length;i++)
			//$("."+templateSelectors[i]).parent().css("cursor", "auto");
		//return;
	//}
	
	//remove the reqId from the array of synchronous requests
	//@todo is there a better way to do this?
	//@todo should this also be moved out of ajaxComplete?
	var newRequests = new Array();
	var count = 0;
	for (var i=0;i<ajaxSyncRequests.length;i++) {
		var found = false;
		//for (var j=0;j<templateSelectors.length;j++) {
		for (var j=0;j<lockSelectors.length;j++) {
			//if (ajaxSyncRequests[i] == templateSelectors[j]) {
			if (ajaxSyncRequests[i] == lockSelectors[j]) {
				found = true;
				break;
			}
		}
		if (!found) {
			newRequests[count] = ajaxSyncRequests[i];
			count++;
		}
	}
	
	ajaxSyncRequests = newRequests;
	
	//for (var i=0;i<templateSelectors.length;i++) {
		//$("."+templateSelectors[i]).parent().css("cursor", "auto");
		
		/*var templateSelector = templateSelectors[i];
		var floatId = templateSelector+"-overlay";
		var container = $("."+templateSelector).parent();
  		container.fadeTo(0,1);
  		$("#"+floatId).remove();*/
	//}
	
	//for (var i=0;i<lockSelectors.length;i++) {
		//var lockSelector = lockSelectors[i];
		//var floatId = lockSelector+"-overlay";
		//var container = $("."+lockSelector).parent();
		
		//removeAjaxOverlay(container);
		
  		//container.fadeTo(0,1);
  		//$("#"+floatId).remove();
  		//container.children(".overlay").remove();
	//}
}

function getSelectors(ajaxIds)
{
	//get the set of template selectors using the ajax ids and send them along in the post
	var templateSelectors = new Array(), lockSelectors = Array();
	var count = 0, countLock = 0;
	for (var i=0;i<ajaxIds.length;i++) {
		//alert("."+ajaxIds[i]+".ajaxSelector");
		$("."+ajaxIds[i]+".ajaxSelector").each(
			function() {
				//className = $(this).parent().children(".templateSelector."+ajaxIds[i]).attr("class");
				//alert($(this).attr("class"));
				//className = $(this).parent().children("."+ajaxIds[i]+".ajaxSelector").attr("class");
				className = $(this).attr("class");
				
				templateSelector = className.substring(0,className.indexOf(" "));
				if (className.indexOf(" lock ") != -1) {
					lockSelectors[countLock] = templateSelector;
					countLock++;
				}
				templateSelectors[count] = templateSelector;
				count++;
			}
		);
	}
	
	var ret = new Array();
	ret["templateSelectors"] = templateSelectors;
	ret["lockSelectors"] = lockSelectors;
	
	return ret;
}

function getSelectorDataString(templateSelectors)
{
	var first = true, data = '';
	for (var i=0;i<templateSelectors.length;i++) {
		if (!first)
			data += "&";
		first = false;
		
		data += "templateSelectors[]="+templateSelectors[i];
	}
	
	return data;
}

function getElementFormData(elementSelector)
{
	return $(elementSelector).parents("form:first").serialize();
}

//@todo remove the below since it is no longer used..

//function sendAjaxRequest(url,data,ajaxIds,async)
function sendAjaxRequest(url,data,ajaxIds)
{
	//get the set of template selectors using the ajax ids and send them along in the post
	var templateSelectors = new Array(), lockSelectors = Array();
	var count = 0, countLock = 0;
	for (var i=0;i<ajaxIds.length;i++) {
		//alert("."+ajaxIds[i]+".ajaxSelector");
		$("."+ajaxIds[i]+".ajaxSelector").each(
			function() {
				//className = $(this).parent().children(".templateSelector."+ajaxIds[i]).attr("class");
				//alert($(this).attr("class"));
				//className = $(this).parent().children("."+ajaxIds[i]+".ajaxSelector").attr("class");
				className = $(this).attr("class");
				
				templateSelector = className.substring(0,className.indexOf(" "));
				if (className.indexOf(" lock ") != -1) {
					lockSelectors[countLock] = templateSelector;
					countLock++;
				}
				templateSelectors[count] = templateSelector;
				count++;
			}
		);
	}
	if (data.length != 0)
		data += "&";
	var first = true;
	for (var i=0;i<templateSelectors.length;i++) {
		if (!first)
			data += "&";
		first = false;
		
		data += "templateSelectors[]="+templateSelectors[i];
	}
	
	$.ajax({
		type: "POST",
		url: url,
		data: data,
		dataType: "text",
		cache: false,
		beforeSend: 
			function(xhr,settings)
			{
				//return checkAjaxRequest(xhr,settings,templateSelectors,async);
				return checkAjaxRequest(xhr,settings,templateSelectors,lockSelectors);
			},
		error: 
			function(xhr,status,error)
			{
				//ajaxError(xhr,status,error,templateSelectors,async);
				ajaxError(xhr,status,error,templateSelectors,lockSelectors);
			},
		success: 
			function(text,status,xhr)
			{
				//ajaxSuccess(data,status,xhr,templateSelectors,async);
				ajaxSuccess(text,status,xhr,templateSelectors,lockSelectors);
			},
		complete:
			function(xhr,status)
			{
				//ajaxComplete(xhr,status,templateSelectors,async);
				ajaxComplete(xhr,status,templateSelectors,lockSelectors);
			}
	});
}

//load an ajax request on an action
function actionAjax(elementSelector,eventType,requestId,submitData,ajaxIds,url)
{
	//@todo surely this is unnecessary: when the elements are reloaded their previous ajax handlers would have been destroyed...
	//need to make sure we only have one of this specific ajax request per element
	$(elementSelector).unbind(eventType+"."+requestId);

	$(elementSelector).bind(eventType+"."+requestId,
		function(event)
		{
			//for a keyup only submit the request if the key was alnum - otherwise it can be chaos
			//@todo this should only be included for specific keyup ajax requests (not necessarily all of them)
			if (eventType == "keyup") {
				var char = String.fromCharCode(event.which);
				if (!isAlpha(char))
					return;
			}
			
			if (!submitData) {
				data = '';
			}
			else {
				//@todo this can only apply to form elements so it should only be loaded for form element ajax requests
				data = $(elementSelector).parents("form:first").serialize();
			}
			
			//won't stop JS event propagation
			//@todo should only be applied to events where it makes sense (i.e. click and submit (?))
			event.preventDefault();
			
			sendAjaxRequest(url,data,ajaxIds);
		}
	);
}
