// define various motion functions here
// Motion functions have x/y scale 0-1
function sigmoid(x) {
	return 1 / ( 1 + Math.exp(6-x * 12) );
}
function dampedSHM(x) {
	return 1 - Math.exp(-4*x)*Math.cos(10*x);
}
function sine(x) {
	return Math.sin(Math.PI*x);
}
function motionExp(x) {
	return 1 - Math.exp(-6*x);
}
function linear(x) { return x; }

var defaultLength = 20;

function Action( start, end, frames ) {
	this.motionFunc = motionExp;
	
	this.start = start;
	this.end = end;
	this.length = frames;
	this.moveList = Array();
	this.suffix = "";
	
	this.displace = this.end-this.start;
		
	for (var i = 0; i < this.length; i++) {
		this.moveList[i] = this.displace * this.motionFunc(i / this.length) + this.start;
	}
}
Action.prototype.get = function (frame) { 
	if (frame < 0)
		return this.start + this.suffix;
	if (frame >= this.length)
		return this.end + this.suffix;
	else
		if (this.displace <= 1) {
			return Math.round(this.moveList[frame]*100)/100 + this.suffix;
		} else
			return Math.round(this.moveList[frame]) + this.suffix;
}
Action.prototype.setSuffix = function ( suf ) { this.suffix = suf; }
	
function ActionColor( start, end, frames) {
	this.start = parseColor(start);
	this.end = parseColor(end);
	this.length = frames;
	this.actions = Array();
	
	this.init = function () {
		for (var i = 0; i < 3; i++)
			this.actions[i] = new Action(this.start[i], this.end[i], this.length);
	}
	
	this.get = function (frame) {
		if (frame < 0)
			return printColor(this.start);
		else if (frame >= this.length)
			return printColor(this.end);
		else {
			return "rgb("+this.actions[0].get(frame)+","+this.actions[1].get(frame)+","+this.actions[2].get(frame)+")";
		}
	}
	
	this.init();
}

function Animation( obj ) {
	if (typeof(obj) == "object") 
		this.obj = obj;
	else
		this.obj = document.getElementById(obj);
	
	this.actions = Array();
	this.length = 0;
	this.timerID = -1;
	this.curFrame = 0;
}
Animation.prototype.getAttribute = function ( attribute ) { 
	if (typeof(this.obj.style[attribute]) != "undefined") {
		if (attribute == "color" || attribute == "backgroundColor") {
			return this.obj.style[attribute];
		} else {
			return parseInt(this.obj.style[attribute]); 
		}
	} else if (typeof(this.obj[attribute]) != "undefined") {
		return parseInt(this.obj[attribute]);
	} else if (attribute == "opacity" && this.obj.filters) {
		if (this.obj.filters.item("DXImageTransform.Microsoft.Alpha")) {
			return this.obj.filters.item("DXImageTransform.Microsoft.Alpha").opacity / 100;
		} else {
			return 1;
		}
	} else {
		throw new Exception("Invalid initial attribute; use setAttribute to set initial! "+ attribute);
	}
}
Animation.prototype.setAttribute = function ( attribute , value ) {
	if (this.obj.filters && attribute == "opacity" ) {
		this.obj.style.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity="+(value*100)+")";
	} else if (typeof(this.obj.style[attribute]) != "undefined") {
		this.obj.style[attribute] = value;
	} else if (typeof(this.obj[attribute]) != "undefined") {
		this.obj[attribute] = value;
	}
}
Animation.prototype.setMovement = function ( attribute, endKey, length ) {
	this.curFrame = 0;
	window.clearInterval(this.timerID);
	
	if (typeof(length) == "undefined") 
		length = defaultLength;
	if (this.length < length)
		this.length = length;
	
	if (attribute == "color" || attribute == "backgroundColor") {
		this.actions[attribute] = new ActionColor(this.getAttribute(attribute), endKey, length);
	} else {
		var endval = parseInt(endKey);
		var suffix = "";
		if (typeof(endKey) == "string") 
			suffix = endKey.replace(endval, "");
		
		this.actions[attribute] = new Action(this.getAttribute(attribute), endval, length);
		this.actions[attribute].setSuffix(suffix);
	}
}
Animation.prototype.setFrame = function () {
	this.curFrame++;
	for (var i in this.actions) {
		if (this.actions[i].length > this.curFrame)
			this.setAttribute(i, this.actions[i].get(this.curFrame));
		else {
			window.clearInterval(this.timerID);
			if (this.endFunc)
				this.endFunc();
		}
	}
}
Animation.prototype.start = function (len) {
	window.clearInterval(this.timerID);
	var thisObj = this;	
	if (len) {
		for (var i = 0; i < this.length; i++) 
			window.setTimeout(function() { thisObj.setFrame(); }, i*len/this.length);
	} else 
		this.timerID = window.setInterval(function() { thisObj.setFrame(); }, 1);	
}
Animation.prototype.setEndFunc = function (func) { this.endFunc = func; }

parseColor = function (color) {
	var colorArr = Array();
	if (color.substr(0,1) == "#")
		color = color.substr(1);
	
	if (color.length == 6) {
		colorArr[0] = parseInt(color.substr(0,2), 16);
		colorArr[1] = parseInt(color.substr(2,2), 16);
		colorArr[2] = parseInt(color.substr(4,2), 16);
	} else if ( color.length == 3 ) {
		colorArr[0] = parseInt(color.substr(0,1)+color.substr(0,1), 16);
		colorArr[0] = parseInt(color.substr(1,1)+color.substr(1,1), 16);
		colorArr[0] = parseInt(color.substr(2,1)+color.substr(2,1), 16);
	} else if ( color.substr(0,3) == "rgb") {
		color = color.substr(4, color.length-5);
		colorArr = color.split(",");
		for (i in colorArr)
			colorArr[i] = parseInt(colorArr[i]);
	} else {
		throw new Exception("Invalid color");
	}
	return colorArr;
}
printColor = function (color) {
	return "rgb("+color[0]+","+color[1]+","+color[2]+")";
}

function Exception(msg) {
	this.message = msg;
	this.toString = function () {
		return this.message;
	}
}
