

var Anim;

if(!Anim) Anim = Class.create();

Anim.Direction = {Forward: 1, Backward: 0};

Anim.Eases = {	
	Linear: function(x) {
		return x;
	},
	
	Parabolic: function(x) {
		return x * x;
	},
		
	InvertedParabolic: function(x) { 
		return x * (2.0 - x); 
	},
	
	InverseParabolic: function(x) {
		return Math.sqrt(Math.abs(x));
	},
	
	Sinusoidal: function(x) {
		return Math.sin(Math.PI * x / 2.0);
	}
};

Anim.currentMilliseconds = function() { return (new Date()).getTime(); };

Anim.Animation = Class.create();

Anim.Animation.prototype = {
	initialize: function(options) {
		this.framesPerSecond = 25;
		this.totalFrames = 25;
		this.direction = Anim.Direction.Forward;
		
		this.currentFrame = 0;
		
		this.handle = null;
		this.isAnimating = false;
				
		this.setOptions(options);
	},
		
	setOptions: function(options) {
		for(var x in options)
			this[x] = options[x];
	},
	
	setDirection: function(dir) {
		this.direction = dir;
	},
	
	gotoEnd: function() {
		this.stop();
		
		this.currentFrame = this.totalFrames;
		this.draw(this.currentFrame, this.direction);
	},
	
	gotoBegin: function() {
		this.stop();
		
		this.currentFrame = 0;
		this.draw(this.currentFrame, this.direction);
	},
	
	gotoFrame: function(frame) {
		this.currentFrame = frame;
		
		if(!this.isAnimating) 
			this.draw(this.currentFrame, this.direction);
	},
	
	getPercentComplete: function() {
		return 1.0 * this.currentFrame / this.totalFrames;
	},
	
	
	toggleAndStart: function() {
		if(this.currentFrame <= 0 && this.direction == Anim.Direction.Backward)
			this.reverse();
		else if(this.currentFrame >= this.totalFrames && this.direction == Anim.Direction.Forward)
			this.reverse();
		else if(this.currentFrame > 0 && this.currentFrame < this.totalFrames)
			this.reverse();
		
		this.start();
	},
	
	start: function() {
		var this_ = this;
		
		if(!this.isAnimating) {		
			this.isAnimating = true;
					
			var msecs = Math.floor(1000.0 / this.framesPerSecond);
			
			this.handle = setInterval(function() { this_.drawFrame(); }, msecs);
			
			if(this.onstart) this.onstart(this.direction);
		}
	},
	
	reverse: function() {
		if(this.direction == Anim.Direction.Backward) 
			this.direction = Anim.Direction.Forward;
		else 
			this.direction = Anim.Direction.Backward;
	},
		
	drawFrame: function() {
		if(this.direction == Anim.Direction.Backward)
			this.currentFrame--;
		else this.currentFrame++;
	
		if(this.currentFrame >= this.totalFrames) {
			this.currentFrame = this.totalFrames;
			this.stop();
		}
		else if(this.currentFrame <= 0) {
			this.currentFrame = 0;
			this.stop();
		}
		
		this.draw(this.currentFrame, this.direction);
	},
	
	stop: function() {
		clearInterval(this.handle);
		this.handle = null;

		this.isAnimating = false;
		
		if(this.onstop) this.onstop(this.direction);
	},
	
	/* virtual */ draw: function(frame, direction) {  }
};

Anim.Trans = Class.create();

/* virtual */ Anim.Trans.TransitionBase = Class.create();
Anim.Trans.TransitionBase.prototype = {
	initialize: function() {},
	/* virtual */ draw: function(element, percent) {}
};

Anim.Trans.Transition = Class.create();
Anim.Trans.Transition.prototype = Object.extend(new Anim.Animation(), {
	initialize: function(element, transitions, options) {
		var this_ = this;
		
		this.element = element;
		if(typeof element == "string")
			this.element = $(element);
			
		this.transitions = transitions;
		this.easeFunction = Anim.Eases.Linear;
		if(options && options.easeFunction) 
			this.easeFunction = options.easeFunction
		
		this.actuators = [];
		if(options && options.actuators)
			this.actuators = options.actuators;
		
		this.defaultActivation = "click-toggle";
		if(options && options.defaultActivation) 
			this.defaultActivation = options.defaultActivation;
			
		this.actuators.each(function(a) {
			this_.setupActuator(a, this_.defaultActivation);
		});
		
		this.setOptions(options);
	},
	
	setupActuator: function(actuator, activation) {
		var this_ = this;
		
		switch(activation) {
		case "click-toggle":
			Event.observe(actuator, "click", function() { this_.toggleAndStart(); });
			break;
		case "hover-on":
			Event.observe(actuator, "mouseover", function() { this_.playForward(); });
			Event.observe(actuator, "mouseout", function() { this_.playReverse(); });
			break;
		}
	},
	
	draw: function(frame,direction) {
		//trace("draw Transition");
		var this_ = this;
		this_.transitions.each(function(t) {
			t.draw(this_.element, this_.easeFunction(frame / this_.totalFrames));
		});
	},
	
	playForward: function() {
		if(this.direction != Anim.Direction.Forward) {
			this.setDirection(Anim.Direction.Forward);
			if(this.isAnimating) this.stop();
		}
		this.start();
	},
	playReverse: function() {
		if(this.direction != Anim.Direction.Backward) {
			this.setDirection(Anim.Direction.Backward);
			if(this.isAnimating) this.stop();
		}
		this.start();
	},
	pause: function() {
		this.stop();
	},
	
	play: function() {
		this.start();
	},
	
	togglePause: function() {
		if(!this.isAnimating) this.play();
		else this.pause();
	}
});

Anim.Trans.Slide = Class.create();
Anim.Trans.Slide.prototype = Object.extend(new Anim.Trans.TransitionBase(), {
	initialize: function(startPosition, endPosition) {
		this.startPosition = startPosition;
		this.endPosition = endPosition;
	},
	
	draw: function(element, percent) {
		element.makePositioned().setStyle({
			left: this.endPosition.x * percent + this.startPosition.x * (1.0 - percent),
			top:  this.endPosition.y * percent + this.startPosition.y * (1.0 - percent)
		});
	}
});

Anim.Trans.Fade = Class.create();
Anim.Trans.Fade.prototype = Object.extend(new Anim.Trans.TransitionBase(), {
	initialize: function(startOpacity, endOpacity) {
		this.startOpacity = startOpacity;
		this.endOpacity = endOpacity;
	},
	
	draw: function(element, percent) {
		element.makePositioned().setStyle({
			opacity: this.endOpacity * percent + this.startOpacity * (1.0 - percent)
		});
	}
});

Anim.Trans.Zoom = Class.create();
Anim.Trans.Zoom.prototype = Object.extend(new Anim.Trans.TransitionBase(), {
	initialize: function(startSize, endSize) {
		this.startSize = startSize;
		this.endSize = endSize;
	},
	
	draw: function(element, percent) {
		element.setStyle({
			width:  this.endSize.width * percent + this.startSize.width * (1.0 - percent),
			height: this.endSize.height * percent + this.startSize.height * (1.0 - percent)
		});
	}
});

Anim.Trans.DiscreteHide = Class.create();
Anim.Trans.DiscreteHide.prototype = Object.extend(new Anim.Trans.TransitionBase(), {
	initialize: function(hideBefore, hideAfter, initial) {
		this.hideBefore = hideBefore;
		this.hideAfter = hideAfter;
		this.displayed = initial;		
	},
	
	draw: function(element, percent) {
		// the default should be visible
		if( !(percent <= this.hideBefore) && !(percent >= this.hideAfter) && !this.displayed) {
			element.setStyle({visibility: 'visible'});
			this.displayed = true;
		}
		else if((percent <= this.hideBefore || percent >= this.hideAfter ) && (this.displayed || this.displayed == undefined)) { 
			element.setStyle({visibility: 'hidden'});
			this.displayed = false;
		}
	}
});


