/*
Author/Autor: 
	Carlos Andres Caballero Pino, Panama   2007
	version 2.0 2007
*/
function jsAnimable(target){
	target=jsObject(target);
	if(!target._js_anim_pool)	target._js_anim_pool=[];
	target.addAnimation=function(name){
		this._js_anim_pool[name]=new jsAnimation(target);
		return this._js_anim_pool[name];
	}
	target.animation=function(name){
		return this._js_anim_pool[name];
	}
	return target;
}
function jsAnimation(target,fps,loop){
	this._target=target;
	this.fps=!isNaN(fps)?fps:30;
	this.loop=loop;
	this._current_frame=0;
	this._frame_listeners=[];
	this.frameCount=0;
	this._timer=null;
	this._paused=false;
	this._chanels={
		position:{
			keys:[],
			idx:null,
			_current_seed:0,
			_permited:{
				'left':true,
				'top':true,
				'width':true,
				'height':true			
				}
			},
		style:{
			keys:[],
			idx:null,
			_current_seed:0,
			_permited:{
				'backgroundColor':true,
				'color':true,
				'opacity':true
				}
			}
	}
	this.play=function(){
		if(this._current_frame<=this.frameCount){
			if(!this._paused){
				this._update();
				this._current_frame++;				
			}
			var tg=this;
			this._timer=window.setTimeout(function(){ tg.play() },1000/this.fps);
		}else{
			if(!this.loop){
				this.stop();
			}else{
				this._current_frame=0;
				this._chanels.style._current_seed=0;
				this._chanels.position._current_seed=0;
				this.play();
			}
		}		
		return this;
	}
	this.stop=function(){
		this._current_frame=0;
		this._chanels.style._current_seed=0;
		this._chanels.position._current_seed=0;
		window.clearTimeout(this._timer);
		this._paused=false;
		this._timer=null;
		return this;
	}
	this.pause=function(){
		this._paused=!this._paused;		
		return this;
	}
	this.goTo=function(frame){
		this.stop();
		frame=parseInt(frame);
		if(frame<=this.frameCount && frame>=0){
			for(var i=0;i<=frame;i++){
				this._current_frame=i;
				this._update();
			}
		}
		return this;
	}
	this.goToAndPlay=function(frame){
		this.stop();
		frame=parseInt(frame);
		if(frame<=this.frameCount && frame>=0){
			this.goTo(frame);
			this.play();
		}
		return this;
	}
	this.setKeyFrame=function(type,frame,values){  //type must be 'position','style'
		var prev;
		switch(type){
			case 'style':
				prev=this._chanels.style.idx;
				this._chanels.style.keys[frame]={
						prev:prev,
						next:null,
						state:values
				};
				if(this._chanels.style.keys[prev]){
					this._chanels.style.keys[prev].next=frame;					
				}
				this._chanels.style.idx=frame;
			break;
			default:
				prev=this._chanels.position.idx;
				this._chanels.position.keys[frame]={
						prev:prev,
						next:null,
						state:values
				};
				if(this._chanels.position.keys[prev]){
					this._chanels.position.keys[prev].next=frame;					
				}
				this._chanels.position.idx=frame;
			break;
		}
		this.frameCount=Math.max(this.frameCount,frame);
		return this;
	}
	//frame interaction
	this.addFrameListener=function(frame,func){
		if(jsLib.isFunction(func)){
			this._frame_listeners[frame]=func;
		}else{
			jsLib.objectError('jsAnimation','Invalid Argument for addFrameListener')
		}
		return this;
	}
/*
###################### This is where all the job is done ####################
*/
	this._update=function(){
		//style channel animation
		var frame_dif=0;
		var tframe=0;
		var slope=0;
		var org=this._chanels.style.keys[this._chanels.style._current_seed];
		if(org){
			var apply={};
			tframe=this._current_frame-this._chanels.style._current_seed;
			if(this._current_frame>0){
				frame_dif=Math.abs(org.next-this._chanels.style._current_seed);				
				if(org.next){					
					var dst=this._chanels.style.keys[org.next];
					if(this._current_frame==org.next){
						this._chanels.style._current_seed=org.next;
						apply=dst.state;
					}else{
						var dstval=dst.state;
						
						for(s in org.state){
							if(dstval[s]){
								switch(s){
									case 'backgroundColor':
										apply[s]=this._color_interpolate(org.state[s],dstval[s],frame_dif,tframe);
									break;
									case 'color':
										apply[s]=this._color_interpolate(org.state[s],dstval[s],frame_dif,tframe);
									break;
									case 'opacity':																														
										this._target.setOpacity(this._interpolate(org.state[s],dstval[s],frame_dif,tframe));
									break;
									default:
										document.title=org.next+' '+this._current_frame;
									break;
								}
							}
						}
					}
				}
			}else{
				apply=org.state;
			}
			this._target.setStyle(apply);
		}
		//position channel
		frame_dif=0;
		tframe=0;
		slope=0;
		org=this._chanels.position.keys[this._chanels.position._current_seed];
		if(org){
			var apply={};
			tframe=this._current_frame-this._chanels.position._current_seed;
			if(this._current_frame>0){
				frame_dif=Math.abs(org.next-this._chanels.position._current_seed);
				if(org.next){
					var dst=this._chanels.position.keys[org.next];
					if(this._current_frame==org.next){
						this._chanels.position._current_seed=org.next;
						apply=dst.state;
					}else{
						var dstval=dst.state;
						for(s in org.state){
							if(dstval[s] && this._chanels.position._permited[s]){
								apply[s]=this._interpolate(parseInt(org.state[s].toString().replace(/px/,'')),parseInt(dstval[s].toString().replace(/px/,'')),frame_dif,tframe)+'px';								
							}
						}
					}
				}
			}else{
				apply=org.state;
			}
			this._target.setStyle(apply);
		}
		if(this._frame_listeners[this._current_frame]){
			this._frame_listeners[this._current_frame](this)
		}		
	}
	this._color_interpolate=function(ocolor,dst_color,time_diff,ctime){
		var colorA=jsLib.colorOps.getRGB(ocolor);
		var colorB=jsLib.colorOps.getRGB(dst_color);
		return jsLib.colorOps.getHEX(
									 this._interpolate(colorA.r,colorB.r,time_diff,ctime),
									 this._interpolate(colorA.g,colorB.g,time_diff,ctime),
									 this._interpolate(colorA.b,colorB.b,time_diff,ctime)
									 );
	}
	this._interpolate=function(v1,v2,time_diff,ctime){
		return 	(((v2-v1)/time_diff)*ctime)+v1;
	}	
}