var topOfQueue_JD = 0;
var queueObject_JD = new Array();
var queueProperty_JD = new Array();
var queueStart_JD = new Array();
var queueEnd_JD = new Array();
var queueExtension_JD = new Array();
var queueInterval_JD = new Array();
var queueFrames_JD = new Array();
var queueEffect_JD = new Array();
var queueWait_JD = new Array();
var queueOnStart_JD = new Array();
var queueOnComplete_JD = new Array();
var queueAction_JD = new Array();
var queueActionFrame_JD = new Array();
var queueActive_JD = new Array();
var queueTimer_JD = new Array();
var queueCurrentFrame_JD = new Array();
var queueStartTime_JD = new Array();

var nextTimer_JD  = 0;
var timer_JD = new Array();
var timerInterval_JD = new Array();

var queueFrameValue_JD  = new Array(1100);
for (i = 0; i < 1100; i++){
	queueFrameValue_JD[i] = new Array(1100);
}

var animator_sineArray_JD = new Array(360);
var animator_pi_JD = Math.PI;
var animator_radians_JD = 0;

for(var i = 1; i <= 360; i++){
	animator_radians_JD = i * (animator_pi_JD/180);
	animator_sineArray_JD[i]  = Math.sin(animator_radians_JD);
}

/*------------------------------------------------------------------
This function proccess the inputs of an animation request.
------------------------------------------------------------------*/
function animate_JD(params)
{
	/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
	Avalible params --------------------------------------
	>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
	
	multiple inputs are seperated by a '|'.
	ALL inputs must be input as strings.
	
	REQUIRED
	- Objects (The object(s) being referenced)
	- properties (The property/properties being referenced)
		
	OPTIONAL
	- start (The start value for the properites)
	- end (The end value for the properites)
	- extension (The unit that is used to calculate animations)
	- interval (The interval to run the timer with)
	- frames (The number of frames to run the animation over)
	- effect (The effect that the animation is based upon)
	- wait (Wait for a specific frame to start or until a certain pos in the queue e.g '-1','4','0')
	- clearQueue (removes all other aniamtions from the queue - deafult is false)
	- onStart (A function to be run just before animation has begun)
	- onComplete (A function to be run once the animation has been completed)
	- action (A function to be run at a specified frame)
	- actionFrame (Specifies the frame at which to perform an action)
	_______________________________________________________
	_______________________________________________________*/
	
	
	if(params.objects == undefined){
		alert("JD System Design Animator API Error!  No object has been specified.");
		return;
	}
	
	if(params.properties == undefined){
		alert("JD System Design Animator API Error!  No property has been specified.");
		return;	
	}
	
	var objects = params.objects;
	var properties = params.properties;
	var start =  params.start || "0";
	var end = params.end || "0";
	var extension = params.extension || "";
	var interval = params.interval || "20";
	var frames = params.frames || "20";
	var effect = params.effect || "SFS";
	var wait = params.wait || "false";
	var clearQueue = params.clearQueue || "false";
	var onStart = params.onStart || "";
	var onComplete = params.onComplete || "";
	var action = params.action || "";
	var actionFrame = params.actionFrame || "0";
	
	objects = trim_JD(objects);
	properties = trim_JD(properties);
	start = trim_JD(start);
	end = trim_JD(end);
	extension = trim_JD(extension);
	interval = trim_JD(interval);
	frames = trim_JD(frames);
	effect = trim_JD(effect);
	wait = trim_JD(wait);
	clearQueue = trim_JD(clearQueue);
	onStart = trim_JD(onStart);
	onComplete = trim_JD(onComplete);
	action = trim_JD(action);
	actionFrame = trim_JD(actionFrame);

	var numObjects = getNumInputs_JD(objects);
	
	if(numObjects != getNumInputs_JD(properties)){
		alert("JD System Design Animator API Error!  You must set the same number of objects and properties.");
		return;	
	}
	
	var tempNum;
	
	
	if(clearQueue == "true"){clearQueue_JD();}
	
	
	//_____________________________________________________
	//format all inputs ready to be inserted into the queue
	if((tempNum = getNumInputs_JD(start)) < numObjects){
		start = addProperties(start,numObjects-tempNum);
	}else if(tempNum > numObjects){
		start = removeProperties(start,tempNum - numObjects);
	}
	
	if((tempNum = getNumInputs_JD(end)) < numObjects){
		end = addProperties(end,numObjects-tempNum);
	}else if(tempNum > numObjects){
		end = removeProperties(end,tempNum - numObjects);
	}
	
	if((tempNum = getNumInputs_JD(extension)) < numObjects){
		extension = addProperties(extension,numObjects-tempNum);
	}else if(tempNum > numObjects){
		extension = removeProperties(extension,tempNum - numObjects);
	}
	
	if((tempNum = getNumInputs_JD(interval)) < numObjects){
		interval = addProperties(interval,numObjects-tempNum);
	}else if(tempNum > numObjects){
		interval = removeProperties(interval,tempNum - numObjects);
	}
	
	if((tempNum = getNumInputs_JD(frames)) < numObjects){
		frames = addProperties(frames,numObjects-tempNum);
	}else if(tempNum > numObjects){
		frames = removeProperties(frames,tempNum - numObjects);
	}
	
	if((tempNum = getNumInputs_JD(effect)) < numObjects){
		effect = addProperties(effect,numObjects-tempNum);
	}else if(tempNum > numObjects){
		effect = removeProperties(effect,tempNum - numObjects);
	}
	
	if((tempNum = getNumInputs_JD(wait)) < numObjects){
		wait = addProperties(wait,numObjects-tempNum);
	}else if(tempNum > numObjects){
		wait = removeProperties(wait,tempNum - numObjects);
	}
	
	if((tempNum = getNumInputs_JD(onStart)) < numObjects){
		onStart = addProperties(onStart,numObjects-tempNum);
	}else if(tempNum > numObjects){
		onStart = removeProperties(onStart,tempNum - numObjects);
	}
	
	if((tempNum = getNumInputs_JD(onComplete)) < numObjects){
		onComplete = addProperties(onComplete,numObjects-tempNum);
	}else if(tempNum > numObjects){
		onComplete = removeProperties(onComplete,tempNum - numObjects);
	}
	
	if((tempNum = getNumInputs_JD(action)) < numObjects){
		action = addProperties(action,numObjects-tempNum);
	}else if(tempNum > numObjects){
		action = removeProperties(action,tempNum - numObjects);
	}
	
	if((tempNum = getNumInputs_JD(actionFrame)) < numObjects){
		actionFrame = addProperties(actionFrame,numObjects-tempNum);
	}else if(tempNum > numObjects){
		actionFrame = removeProperties(actionFrame,tempNum - numObjects);
	}
	
	
	
	
	//_________________________________
	//insert all objects into the queue
	for(var i = 0; i < numObjects; i++){
		queueObject_JD[topOfQueue_JD] = getRequiredParam_JD(objects, i);
		queueProperty_JD[topOfQueue_JD] = getRequiredParam_JD(properties, i);
		queueStart_JD[topOfQueue_JD] = getRequiredParam_JD(start, i);
		queueEnd_JD[topOfQueue_JD] = getRequiredParam_JD(end, i);
		queueExtension_JD[topOfQueue_JD] = getRequiredParam_JD(extension, i);
		queueInterval_JD[topOfQueue_JD] = getRequiredParam_JD(interval, i);
		queueFrames_JD[topOfQueue_JD] = getRequiredParam_JD(frames, i);
		queueEffect_JD[topOfQueue_JD] = getRequiredParam_JD(effect, i);
		queueWait_JD[topOfQueue_JD] = getRequiredParam_JD(wait, i);
		queueOnStart_JD[topOfQueue_JD] = getRequiredParam_JD(onStart, i);
		queueOnComplete_JD[topOfQueue_JD] = getRequiredParam_JD(onComplete, i);
		queueAction_JD[topOfQueue_JD] = getRequiredParam_JD(action, i);
		queueActionFrame_JD[topOfQueue_JD] = getRequiredParam_JD(actionFrame, i);
		queueCurrentFrame_JD[topOfQueue_JD] = 1;
		queueActive_JD[topOfQueue_JD] = "waiting";
		
		generateFrameValues(queueEffect_JD[topOfQueue_JD], queueStart_JD[topOfQueue_JD], queueEnd_JD[topOfQueue_JD], queueFrames_JD[topOfQueue_JD]);
		startNew();
		topOfQueue_JD++;
	}
}


/*------------------------------------------------------------------
This function will trim_JD a string
------------------------------------------------------------------*/
function trim_JD(string){
  return string.replace(/\s+/g,'');
}


/*------------------------------------------------------------------
This function generate the value required for each frame
------------------------------------------------------------------*/
function generateFrameValues(effect, start, end, totalFrames){
	start = start*1;
	end = end * 1;
	
	switch(effect){
		case "linier":
			for(var i = 1; i <= totalFrames; i++){
				queueFrameValue_JD[topOfQueue_JD][i] = 100/totalFrames;	
			}
		break;
		
		case "SFS":
			var step = 179/totalFrames;
			var total = 0;
			for(var i = 1; i <= totalFrames; i++){
				var current = Math.round(i*step);
				queueFrameValue_JD[topOfQueue_JD][i] = animator_sineArray_JD[current];
				total = total + queueFrameValue_JD[topOfQueue_JD][i];
			}
			
			var multiplier = 100 / total;
			for(var i = 1; i <= totalFrames; i++){
				queueFrameValue_JD[topOfQueue_JD][i] = queueFrameValue_JD[topOfQueue_JD][i] * multiplier;
			}
		break;
		
		case "slow":
			var step = 89/totalFrames;
			var total = 0;
			for(var i = 1; i <= totalFrames; i++){
				var current = Math.round(i*step);
				queueFrameValue_JD[topOfQueue_JD][i] = animator_sineArray_JD[current];
				total = total + queueFrameValue_JD[topOfQueue_JD][i];
			}
			
			var multiplier = 100 / total;
			for(var i = 1; i <= totalFrames; i++){
				queueFrameValue_JD[topOfQueue_JD][i] = queueFrameValue_JD[topOfQueue_JD][i] * multiplier;
			}
		break;
		
		case "speed":
			var step = 89/totalFrames;
			var total = 0;
			for(var i = 1; i <= totalFrames; i++){
				var current = Math.round(i*step);
				queueFrameValue_JD[topOfQueue_JD][i] = animator_sineArray_JD[current+90];
				total = total + queueFrameValue_JD[topOfQueue_JD][i];
			}
			
			var multiplier = 100 / total;
			for(var i = 1; i <= totalFrames; i++){
				queueFrameValue_JD[topOfQueue_JD][i] = queueFrameValue_JD[topOfQueue_JD][i] * multiplier;
			}
		break;
		
		
		case "spring":
			var section1 = Math.round((totalFrames / 100) * 40);
			var section2 = Math.round((totalFrames / 100) * 30);
			var section3 = Math.round((totalFrames / 100) * 30);
			
			var step = 179/section1;
			var total = 0;
			for(var i = 1; i <= section1; i++){
				var current = Math.round(i*step);
				queueFrameValue_JD[topOfQueue_JD][i] = animator_sineArray_JD[current];
				total = total + queueFrameValue_JD[topOfQueue_JD][i];
			}
			
			var multiplier = 120 / total;
			for(var i = 1; i <= section1; i++){
				queueFrameValue_JD[topOfQueue_JD][i] = queueFrameValue_JD[topOfQueue_JD][i] * multiplier;
			}

			var step = 179/(totalFrames-section1+section2);
			var total = 0;
			for(i = section1+1; i <= section1+section2; i++){
				var current = Math.round(i*step);
				queueFrameValue_JD[topOfQueue_JD][i] = animator_sineArray_JD[current];
				total = total + queueFrameValue_JD[topOfQueue_JD][i];
			}
				
			var multiplier = -30 / total;
			for(i = section1+1; i <= section1+section2; i++){
				queueFrameValue_JD[topOfQueue_JD][i] = queueFrameValue_JD[topOfQueue_JD][i] * multiplier;
			}
			
			var step = 179/(totalFrames-section3);
			var total = 0;
			for(i = section1+section2; i <= totalFrames; i++){
				var current = Math.round(i*step);
				queueFrameValue_JD[topOfQueue_JD][i] = animator_sineArray_JD[current];
				total = total + queueFrameValue_JD[topOfQueue_JD][i];
			}
						
			var multiplier = 10 / total;
			for(i = section1+section2; i <= totalFrames; i++){
				queueFrameValue_JD[topOfQueue_JD][i] = queueFrameValue_JD[topOfQueue_JD][i] * multiplier;
			}
		
			
		break;
		
		case "hesitate":
			var step = 359/totalFrames;
			var total = 0;
			for(var i = 1; i <= totalFrames; i++){
				var current = Math.round(i*step);
				queueFrameValue_JD[topOfQueue_JD][i] = animator_sineArray_JD[current] +1;
				total = total + queueFrameValue_JD[topOfQueue_JD][i];
			}
			
			var multiplier = 100 / total;
			for(var i = 1; i <= totalFrames; i++){
				queueFrameValue_JD[topOfQueue_JD][i] = queueFrameValue_JD[topOfQueue_JD][i] * multiplier;
			}
		break;
		
		case "quickStop":
			var step = 90/totalFrames;
			var total = 0;
			for(var i = 1; i <= totalFrames; i++){
				var current = Math.round(i*step);
				queueFrameValue_JD[topOfQueue_JD][i] = animator_sineArray_JD[current];
				total = total + queueFrameValue_JD[topOfQueue_JD][i];
			}
			
			var multiplier = 100 / total;
			for(var i = 1; i <= totalFrames; i++){
				queueFrameValue_JD[topOfQueue_JD][i] = queueFrameValue_JD[topOfQueue_JD][i] * multiplier;
			}
		break;
		
		
	}
	
	
	if(start < end){
		if(start < 0){
			var offset = start;
			start = start - offset;
			end = end - offset;
		}else{
			var offset = start;
			start = start - offset;
			end = end - offset;
		}
		var difference = end - start;
		backwards=false;
	}else{
		if(end < 0){
			var offset = end;
			end = end - offset;
			start = start - offset;
		}else{
			var offset = end;
			end = end - offset;
			start = start - offset;
		}
		var difference = start - end;
		backwards=true;
	}
	
	var multiplier = difference / 100;

	if(backwards== false){
		var total = 0;
		for(var i = 1; i <= totalFrames; i++){
			total = total + queueFrameValue_JD[topOfQueue_JD][i];
			queueFrameValue_JD[topOfQueue_JD][i] = (total * multiplier) + offset;
		}
	}else{
		var total = 100;
		for(var i = 1; i <= totalFrames; i++){
			total = total - queueFrameValue_JD[topOfQueue_JD][i];
			queueFrameValue_JD[topOfQueue_JD][i] = (total * multiplier) + offset;
		}
	}

}



/*------------------------------------------------------------------
This function will create a new timer at the required interval
------------------------------------------------------------------*/
function createTimer(interval){
	timerInterval_JD[nextTimer_JD] = interval;
	timer_JD[nextTimer_JD] = setInterval("animateFrame("+interval+");",interval);
	nextTimer_JD++;
}


/*------------------------------------------------------------------
This function will check if a timer is set for the required 
interval
------------------------------------------------------------------*/
function timerIsSet(interval){
	var timers = nextTimer_JD - 1;
	for(var i = 0; i <= timers; i++){
		if(timerInterval_JD[i] == interval)
		{
			return i;
		}
	}
	return -1;
}


/*------------------------------------------------------------------
This function will active all items infront of the specified item
------------------------------------------------------------------*/
function activateQueued(position){
	for(var i = position - 1; i >= 0; i--){
		if(queueWait_JD[i] != "2"){
			queueActive_JD[i] = "true";
		}
	}
}

/*------------------------------------------------------------------
This function will add properties to an input
------------------------------------------------------------------*/
function addProperties(input,number){
	input = input + '';
	var properties = new Array();
	properties = input.split("|");
	var singleInput = properties[0]; // e.g 'px'
	
	for(var i = 1; i <= number; i++){
		input = input + "|" + singleInput;
	}
	
	return input;
}


/*------------------------------------------------------------------
This function will remove properties from an input
------------------------------------------------------------------*/
function removeProperties(input,number){
	input = input + '';
	var properties = new Array();
	properties = input.split("|");
	
	var i = 0;
	while(i <= number){
		result = result + properties[i] + "|"
		i++;
	}	
	return result.slice(0, -1);
}


/*------------------------------------------------------------------
This function will remove an finished animation from the queue
------------------------------------------------------------------*/
function removeItemFromQueue(number){
	if (topOfQueue_JD > 1){
		for(var i = number; i < topOfQueue_JD; i++){
			queueObject_JD[i] = queueObject_JD[i+1];
			queueProperty_JD[i] = queueProperty_JD[i+1];
			queueStart_JD[i] = queueStart_JD[i+1];
			queueEnd_JD[i] = queueEnd_JD[i+1];
			queueExtension_JD[i] = queueExtension_JD[i+1];
			queueInterval_JD[i] = queueInterval_JD[i+1];
			queueFrames_JD[i] = queueFrames_JD[i+1];
			queueEffect_JD[i] = queueEffect_JD[i+1];
			queueWait_JD[i] = queueWait_JD[i+1];
			queueOnStart_JD[i] = queueOnStart_JD[i+1];
			queueOnComplete_JD[i] = queueOnComplete_JD[i+1];
			queueAction_JD[i] = queueAction_JD[i+1];
			queueActionFrame_JD[i] = queueActionFrame_JD[i+1];
			queueActive_JD[i] = queueActive_JD[i+1];
			queueTimer_JD[i] = queueTimer_JD[i+1];
			queueCurrentFrame_JD[i] = queueCurrentFrame_JD[i+1];
			queueStartTime_JD[i] = queueStartTime_JD[i+1];
			
			for(var c = 1; c <= 1100; c++){
				queueFrameValue_JD[i][c] = queueFrameValue_JD[i+1][c]; 
			}
		}
	}else{
		queueActive_JD[0] = "false";
		queueTimer_JD[0] = "";
		queueWait_JD[0] = "";
	}
	topOfQueue_JD--;
}

/*------------------------------------------------------------------
This function will remove all items from the queue
------------------------------------------------------------------*/
function clearQueue_JD(){
	for(var i=0; i<topOfQueue_JD; i++){
		if(itemsWithInterval(queueInterval_JD[i]) == false){
			removeTimer(queueInterval_JD[i]);	
		}
		queueObject_JD[i] = "";
		queueProperty_JD[i] = "";
		queueStart_JD[i] = "";
		queueEnd_JD[i] = "";
		queueExtension_JD[i] = "";
		queueInterval_JD[i] = "";
		queueFrames_JD[i] = "";
		queueEffect_JD[i] = "";
		queueWait_JD[i] = "";
		queueOnStart_JD[i] = "";
		queueOnComplete_JD[i] = "";
		queueAction_JD[i] = "";
		queueActionFrame_JD[i] = "";
		queueActive_JD[i] = "";
		queueTimer_JD[i] = "";
		queueCurrentFrame_JD[i] = "";
		queueStartTime_JD[i] = "";
	}
	topOfQueue_JD = 0;
}

/*------------------------------------------------------------------
This function will return true if items are still using the
requested timer.
------------------------------------------------------------------*/
function itemsWithInterval(interval){
	for(var i = 0; i < topOfQueue_JD; i++){
		if(queueInterval_JD[i] == interval){
			return true;
		}
	}
	return false;
}


/*------------------------------------------------------------------
This function will remove a requsted timer
------------------------------------------------------------------*/
function removeTimer(interval){
	var timerNum = timerIsSet(interval);
	clearInterval(timer_JD[timerNum]);
	
	for(i = timerNum; i < nextTimer_JD - 1; i++){
		timer_JD[i] = timer_JD[i+1];
		timerInterval_JD[i] = timerInterval_JD[i+1];
	}
	
	nextTimer_JD --;
}


/*------------------------------------------------------------------
This function will check if a new animation should be started
------------------------------------------------------------------*/
function startNew(){
var change=false;
for(var i = 0; i <= topOfQueue_JD; i++){
	if(queueActive_JD[i] == "true"  || queueWait_JD[i] == undefined){
		continue;
	}
	if(i == queueWait_JD[i] || queueWait_JD[i] == "false"){
		queueActive_JD[i] = "true";
		if (timerIsSet(queueInterval_JD[i]) == -1){
			createTimer(queueInterval_JD[i]);
			chnage=true;
		}
	}else if(queueWait_JD[i].substring(0,1) == "+"){
		var num = queueWait_JD[i].substring(1)*1;
		if(queueActive_JD[i+num] == "true"){
			queueActive_JD[i] = "true";
			if (timerIsSet(queueInterval_JD[i]) == -1){
				createTimer(queueInterval_JD[i]);
				change=true;
			}
		}
	}else if(queueWait_JD[i].substring(0,1) == "-"){
		var num = queueWait_JD[i].substring(1)*1;
		if(queueActive_JD[i-num] == "true"){
			queueActive_JD[i] = "true";
			if (timerIsSet(queueInterval_JD[i]) == -1){
				createTimer(queueInterval_JD[i]);
				change=true;
			}
		}
	}
}
	
if(change == true){
	startNew();	
}}

/*------------------------------------------------------------------
--------------------------------------------------------------------
This function will render animation frames
--------------------------------------------------------------------
------------------------------------------------------------------*/
function animateFrame(interval){
	for(var i = 0; i < topOfQueue_JD; i++){
		if(queueActive_JD[i] != "true" || queueInterval_JD[i] != interval){
			continue;
		}
		
		if(queueCurrentFrame_JD[i] == 1){
			eval(queueOnStart_JD[i]);
			queueStartTime_JD[i] = new Date().getTime();
		}else{
			var currentTime = new Date().getTime();
			var sinceStart = currentTime - queueStartTime_JD[i];
			var requiredFrame = Math.round(sinceStart / queueInterval_JD[i]); 
			if(requiredFrame > queueCurrentFrame_JD[i]){
				queueCurrentFrame_JD[i] = requiredFrame;
				if(queueCurrentFrame_JD[i] > queueFrames_JD[i]){
					queueCurrentFrame_JD[i] = queueFrames_JD[i];
				}
			}
		}
		
		if(queueCurrentFrame_JD[i] == queueActionFrame_JD[i]){
			eval(queueAction_JD[i]);
		}
		
		eval("document.getElementById('"+queueObject_JD[i]+"').style."+queueProperty_JD[i]+"='"+queueFrameValue_JD[i][queueCurrentFrame_JD[i]]+queueExtension_JD[i]+"'");
		
		if(queueProperty_JD[i] == "opacity"){			
			eval("document.getElementById('"+queueObject_JD[i]+"').style.filter = 'alpha(opacity=' + queueFrameValue_JD[i][queueCurrentFrame_JD[i]]*100 + ')'");
		}
		
		queueCurrentFrame_JD[i]++;		
		if (queueCurrentFrame_JD[i] > queueFrames_JD[i]){
			eval(queueOnComplete_JD[i]);
			removeItemFromQueue(i);
			startNew();
			if(itemsWithInterval(interval) == false){
				removeTimer(interval);
			}
		}
		
	}
}




