/*
Loosely based on timeline-25.js
By: A.R.Collins
Latest version:<www.arc.id.au/JsAnimation.html>


General idea:
Als er een update plaatsvindt op een html canvas, moeten alle onderdelen opnieuw worden getekend.
De basis dingen die geanimeerd worden zijn:
- x
- y
- scaleX
- scaleY
- rotation
- color (not implemented)

Metronome, which keeps beating at a steady pace, only stops when there's no animation left.
Animation-objects which are put in an array and fed to the Metronome

Metronome calls on 1 update function which can be used to update the canvas.
In this function the update function loops through the Animation-objects and retrieves the updates values.
The updatefunction itself chooses which parts to update with the Animation-object-properties.



*/

/* Metronome 
	@updateFn the update function to call
	@refreshRate the refreshRate in milliseconds in which the update function is called.
	@easer : object which contains the easing-functions
*/
function Metronome(updateFn, refreshRate, loop, easer){
	this.modes = { PAUSED : 1, STOPPED : 2, PLAYING : 3 }
	if(updateFn==undefined||updateFn==""){
		console.log("No update Function defined!");
		return;
	}
	this.updateFn=updateFn;
	if(refreshRate==undefined){
		refreshRate=20;
	}
	this.refreshRate=refreshRate;
	this.mode = this.modes.STOPPED;
	this.anims= new Array();
	this.totalPassedTime=0;
	this.startTime=0;
	this.easer = easer;
	this.currentAnimationArrayPointer=0;
	this.loop=loop;
}

Metronome.prototype.play = function(){
	if (this.mode == this.modes.PLAYING){
		return;
	}
	if(this.anims.length==0){// nothing to play so no need for an interval.
		return;
	}
	
	var savThis = this;
	if (this.mode == this.modes.STOPPED){ // re-starting after stop
		this.startTime = new Date().getTime();
	}else{
		this.mode = this.modes.PLAYING;
	}
	this.timer = setInterval(function(){savThis.calcUpdate()}, savThis.refreshRate);
}

Metronome.prototype.stop = function(){
	if (this.timer){
		clearInterval(this.timer);
	}
	this.mode = this.modes.STOPPED;
}

Metronome.prototype.pause = function(){
	if (this.timer){
		clearInterval(this.timer);
	}
	this.mode = this.modes.PAUSED;
}


Metronome.prototype.addAnimation = function(animArray){
		// check validity?
	this.anims.push(animArray);
	this.anims[this.anims.length-1][0].finished=false;
	this.anims[this.anims.length-1][0].callbackCalled=false;
	
	/* options are:
		1 geef array. De array bestaat uit animaties die bij elkaar horen en tegelijk worden uitgevoerd. ALs de laatste animatie uit de array is uitgevoerd, 
		wordt de volgende array uitgevoerd. Dat is heel logisch. Geen gedoe met namen 
		
		2 geef animaties op en geef bij elke animatie aan welke op welke volgt. Hier kun je heel specifiek animaties laten volgen op animaties.  Maar je hebt hierdoor wel alle animaties in een 
		enorme rij die je elke keer moet controleren op wie er al klaar is of niet.
		
		vast nog meerdere opties, maar ik ga voor de eerste
	
	*/
}
Metronome.prototype.resetAnims = function(){
	for (var j=0;j<this.anims.length;j++){
		for (var i=0; i<this.anims[j].length; i++){
			this.anims[j][i].finished=false;
			this.anims[j][i].paintLastFrame=false;
			this.anims[j][i].startTime=undefined;
		}
	}
}

Metronome.prototype.clearAnims = function(){
	this.stop();
	this.currentAnimationArrayPointer=0;
	this.anims.length=0;
	this.totalPassedTime=0;
	this.startTime=0;
}

Metronome.prototype.calcUpdate = function(){
	var currentFinished=0;
	if(this.currentAnimationArrayPointer==this.anims.length ){
		if(this.loop){
			this.resetAnims();
			this.currentAnimationArrayPointer=0;
		}else{
			clearInterval(this.timer);
		}
		return;
	}
	
	for (var i=0; i<this.anims[this.currentAnimationArrayPointer].length; i++){
		if(this.anims[this.currentAnimationArrayPointer][i].finished){
			currentFinished++;
			if(!this.anims[this.currentAnimationArrayPointer][i].callbackCalled){
				this.anims[this.currentAnimationArrayPointer][i].callbackCalled=true;
				console.log("Animations passed:"+currentFinished+" duration:"+this.anims[this.currentAnimationArrayPointer][i].duration+" at "+this.totalPassedTime);
				if(this.anims[this.currentAnimationArrayPointer][i].callback){
					var callback=this.anims[this.currentAnimationArrayPointer][i].callback;
					callback();
				}
			}
			continue;
		}
		if(this.anims[this.currentAnimationArrayPointer][i].startTime==undefined){// anim.startTime is always later or equal to this.startTime
			this.anims[this.currentAnimationArrayPointer][i].startTime=new Date().getTime()-this.startTime;
		}
		var duration=this.anims[this.currentAnimationArrayPointer][i].duration;
		var delay = this.anims[this.currentAnimationArrayPointer][i].delay;
		var ease = this.anims[this.currentAnimationArrayPointer][i].ease;
		// calculate percentage of time that has passed for this specificic animation, keeping account of a possible delay in start.
		// For my own mental health, for now animation starts concurrently with main animation.
		var percentage=1;
		if(this.totalPassedTime-this.anims[this.currentAnimationArrayPointer][i].startTime<delay+duration){
			percentage =(this.totalPassedTime-delay-this.anims[this.currentAnimationArrayPointer][i].startTime)/duration;
			if(percentage<0){
				percentage=0;
			}
			//console.log("Index "+this.currentAnimationArrayPointer+" perc:"+percentage+" totalPassed:"+this.totalPassedTime);
		}else{
			//console.log("Total passed time:"+this.totalPassedTime);
			// check if animation has finished then paintLastFrame and then it's 100% finished. Start another.
			if(this.anims[this.currentAnimationArrayPointer][i].paintLastFrame){
				this.anims[this.currentAnimationArrayPointer][i].finished=true;
			}else{
				this.anims[this.currentAnimationArrayPointer][i].paintLastFrame=true;
			}
		}
		this.anims[this.currentAnimationArrayPointer][i].percentage=percentage;
		// use easer object to transform straight percentages into ease-values.
		var easeValue = this.easer[ease](percentage);
		// after this, use easeValue to calculate current values for all properties in animatedObject
		//delete(this.anims[i].currentUpdate);
		var currentUpdate=new Object();
		for(var key in this.anims[this.currentAnimationArrayPointer][i]){
			var val = this.anims[this.currentAnimationArrayPointer][i][key];
			switch(key){
				case "startX":
				currentUpdate.currentX = this.anims[this.currentAnimationArrayPointer][i].startX+(this.anims[this.currentAnimationArrayPointer][i].endX-this.anims[this.currentAnimationArrayPointer][i].startX)*easeValue;
				break;
				case "startY":
				currentUpdate.currentY = this.anims[this.currentAnimationArrayPointer][i].startY+(this.anims[this.currentAnimationArrayPointer][i].endY-this.anims[this.currentAnimationArrayPointer][i].startY)*easeValue;
				break;
				case "startRotation":
				currentUpdate.currentR = this.anims[this.currentAnimationArrayPointer][i].startRotation+(this.anims[this.currentAnimationArrayPointer][i].endRotation-this.anims[this.currentAnimationArrayPointer][i].startRotation)*easeValue;
				break;
			
			}
		}
		this.anims[this.currentAnimationArrayPointer][i].currentUpdate=currentUpdate;
	}
	if(currentFinished==this.anims[this.currentAnimationArrayPointer].length){
		this.currentAnimationArrayPointer++;
	}else{
		this.updateFn(this.anims[this.currentAnimationArrayPointer]);
	}
	this.totalPassedTime=new Date().getTime()-this.startTime;// now we know how much time has passed in the actual animation.
	//TODO : make check if any of the animations is still active, otherwise cancel update/timeline interval
	
}

