class Track{
	constructor( h, tLoop = 0 ){ 
		this.frames		= new Array();
		this.handler	= h;
		this.time		= 0;
		this.time_max	= 0;
		this.last_idx	= 0;
		this.curr_idx	= 0;
		this.time_loop	= tLoop;
	}

	sort(){ 
		this.frames.sort( Track.sort_func );
		this.last_idx = this.frames.length - 1;
		return this;
	}
	add(){ 
		for( let i=0; i < arguments.length; i++ ){
			this.frames.push( arguments[i] );
			if( arguments[i].time > this.time_max ) this.time_max = arguments[i].time;
		}
		return this;
	}


	at( t ){
		let last	= this.frames.length - 1,
			v 		= null,
			f, i ;
		
		if( t >= this.frames[ last ].time ){
			v = this.frames[ last ].value;							// At the end of animation
		}else{
			for( i=last-1; i > -1; i-- ){
				f = this.frames[ i ];
				
				if( f.time == t ){ v = f.value; break; } 			// Exact time, just get the value
				
				if( t > f.time ){
					if( f.value == this.frames[ i+1 ].value )
						v = f.value;								// incase there is a pause in change, don't try to lerp
					else 
						v = Frame.lerp( f, this.frames[ i+1 ], t );	// lerp the value between the two frames
					break;
				}
			}
		}

		//console.log( "TRACK AT", t, "Frame", i );		
		this.value = v;
		return v;
	}

	next( dt ){
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Handle final Frame
		this.time += dt;
		let i = this.last_idx;

		if( this.time >= this.frames[ i ].time ){
			//.....................................
			if( this.time_loop == 0 ){
				this.handler.use_frame( this.frames[ i ] );
				return;
			}

			//.....................................
			let et = this.time_max + this.time_loop;	// Extra time
			if( this.time >= et ){						// Reset back to first frame and let function continue
				this.time		= (this.time - et) % this.time_max;
				this.curr_idx	= 0;
			}else{										// Lerp Between Last to First frame
				let t = (this.time - et) / et;
				this.handler.lerp_frame( t, this.frames[ this.last_idx ], this.frames[ 0 ] );
				return;
			}
		}

		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Check if still between the current frame, else find frames to use.
		i = this.curr_idx;
		if( this.time < this.frames[i].time  || this.time >= this.frames[i+1].time ){
			for( let j = this.last_idx-1; j > -1; j-- ){

				// If we find the exact time, Just exit
				if( this.frames[j].time == this.time ){
					this.curr_idx = j;
					this.hander.use_frame( this.frames[j] );
					return;
				}

				// Found the fames
				if( this.time > this.frame[i].time ){ this.curr_idx = i = j; break; }
			}
		}

		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Lerp Between Frames
		let f0	= this.frames[i],
			f1	= this.frames[i+1],
			t	= ( this.time - f0.time ) / ( f1.time - f0.time );

		this.handler.lerp_frame( t, f0, f1 );
	}
	
	static sort_func( a, b ){ return (a.time == b.time)? 0 : (a.time < b.time) ? -1 : 1;  }
}

class Frame{
	constructor( t, v, e=null ){
		this.time	= t;
		this.value	= v;
		this.easing = e;
	}

	static lerp( f0, f1, t ){
		let n = ( t - f0.time ) / ( f1.time - f0.time );
		
		if( f0.easing ) n = f0.easing( n );
		
		return (1-n) * f0.value + n * f1.value;
	}
}

class FramePose{
	constructor( t ){
		this.bones	= null;
		this.time	= t;
	}

	use_arm( arm ){
		let bLen = arm.bones.length;
		this.bones = new Array( bLen );

		for( let i=0; i < bLen; i++ ) 
			this.bones[i] = arm.bones[i].Node.local.rot.clone();

		return this;
	}

	copy( to, pose, from, mirror=false ){
		let len = to.length;
		for( let i=0; i < len; i++ ){
			this.bones[ to[i] ].copy( pose.bones[ from[i] ].local.rot );
			if( mirror ) this.bones[ to[i] ].mirror_x();
		}
	}
}

class FramePoseHandler{
	constructor( arm ){
		this.arm = arm;
	}

	use_frame( f ){
		let b 	= this.arm.bones,
			len = b.length,
			i;

		for( i=0; i < len; i++ ) b[i].Node.setRot( f.bones[i] );
		this.arm.isModified = true;
	}

	lerp_frame( t, f0, f1 ){
		let b 	= this.arm.bones,
			len = b.length,
			i;

		for( i=0; i < len; i++ ){
			Quat.lerp( 
				f0.bones[i], 
				f1.bones[i],
				t, 
				b[i].Node.local.rot
			).normalize();

			b[i].Node.isModified = true;
		}
		this.arm.isModified = true;
	}
}


/*
//let L = [ 14, 15 ];
//let R = [ 18, 19 ];
// RLeg Animation : B-R A-R M(B-L A-L)
// LLeg Animation : B-L A-L M(B-R A-R)

let f0 = new FramePose( 0 ).use_arm( arm );
let f1 = new FramePose( 1 ).use_arm( arm );
let f2 = new FramePose( 2 ).use_arm( arm );
let f3 = new FramePose( 3 ).use_arm( arm );

f0	.copy( R, PoseB, R, doMirror=false )
	.copy( L, PoseB, L, false )
f1	.copy( R, PoseA, R, false )
	.copy( L, PoseA, L, false )
f2	.copy( R, PoseB, L, true )
	.copy( L, PoseB, R, true )
f3	.copy( R, PoseA, L, true )
	.copy( L, PoseA, R, true )

let track = new Track( new FramePoseHandler(arm), 1 )
	.add( f0, f1, f2, f3 )
	.sort();

track.next( dt );
 */

