/*
	kalleboo's amazing cross-browser animation script
	uses CSS animation if available, otherwise falls back to javascript interval
*/
function findselector(stylesheet, text) {
	for(var r = 0; r<stylesheet.cssRules.length;r++) {
		if (stylesheet.cssRules[r] && stylesheet.cssRules[r].selectorText==text)
			return r;
	}
	
	return null;
}

function arraysubtract(base, subtract) {
	var result = base.slice(0);
	var i = 0;
	while (i<result.length) {
		for(var j in subtract) {
			if (result[i]==subtract[j]) {
				result.splice(i,1);
				continue;
			}
		}
		i++;
	}
	return result;
}

var m = new Element('featureDetect');
m.style.cssText = "-webkit-transform: scale(1); -moz-transform: scale(1); -o-transform: scale(1); -o-animation: 'animate' 2s ease 2;";

var browsername = '';
if ( $defined(m.style['WebkitTransform']))		browsername = '-webkit';
if ( $defined(m.style['MozTransform']))			browsername = '-moz';
if ( $defined(m.style['OTransform']))			browsername = '-o';

function CSSAnimation(stylesheet,override) {
	this.stylesheet = stylesheet;
	this.keyframegroups = new Array();
	this.selectors = new Array();
	
	var m = new Element('featureDetect');
	m.style.cssText = "-webkit-animation: 'animate' 2s ease 2;";
	this.browsertype = $defined(m.style['webkitAnimationName']) ? 'webkit-animation' : 'javascript'; //TODO move up with the other detection+support -moz
	if (typeof override!='undefined')
		this.browsertype = override;
	
	this.registerKeyframes = function(keyframes) {
		keyframes.apply(this.stylesheet, this.browsertype);
		this.keyframegroups.push(keyframes);
	};
	
	this.registerSelector = function(selector) {
		selector.apply(this.stylesheet, this.browsertype);
		this.selectors.push(selector);
	};
	
	if (this.browsertype=='webkit-animation') {
		this.animateToggleClasses = function(items) {
			items.each(function(item) {
				item.element.toggleClass(item.className);
			});
		};
	} else if (this.browsertype=='javascript') {
		this.animateToggleClasses = function(items) {
			var before = new Array();
			var after = new Array();
			//get a list of what selectors applied before we toggled
			this.selectors.each(function(item) {
				var found = $$(item.selectorText);
				if (found.length>0)
					before.push(item.selectorText);
			});
			
			//do the actual toggle
			items.each(function(item) {
				item.element.toggleClass(item.className);
			});
			
			//get a list of what selectors apply now
			this.selectors.each(function(item) {
				var found = $$(item.selectorText);
				if (found.length>0)
					after.push(item.selectorText);
			});
			
			//figure out which classes have been lost (=animations to stop) and gained (=animations to start)
			var lost = arraysubtract(before,after);
			var gained = arraysubtract(after,before);
			
			this.selectors.each(function(selector) {
				lost.each(function(selectorText) {
					if (selector.selectorText==selectorText)
						selector.stopAnimate('javascript');
				});
			});
	
			this.selectors.each(function(selector) {
				gained.each(function(selectorText) {
					if (selector.selectorText==selectorText) {
						selector.startAnimate('javascript');
					}
				});
			});
			
			/*
			var s = '';
			lost.each(function(item) { s += 'lost:'+item+'\n';});
			gained.each(function(item) { s += 'gained:'+item+'\n';});
			alert(s);
			*/
			
		};
	}
}

function CSSAnimKeyframes(name, frames) {
	this.name = name;
	this.frames = frames;
	this.parsedFrames = new Array();
	
	this.apply = function(stylesheet, browsertype) {
		if (browsertype=='webkit-animation') {
			//for animation browsers, just create the CSS
			var webkitcss = '@-webkit-keyframes '+name+' {';
			for (var position in frames) {
				webkitcss += position+'% {'+frames[position].replace('transform',browsername+'-transform')+'} ';
			}
			webkitcss += '}';
			stylesheet.insertRule(webkitcss,stylesheet.cssRules.length);

		} else if (browsertype=='javascript') {
			//for javascript browsers, parse the CSS in preparation for animation
			for(var position in frames) {
				var rawdecs = frames[position].split(';');
				var declarations = new Array();
				
				rawdecs.each(function(dectext) {
						if (dectext.trim()!='') {
						var bits = dectext.split(':');
						var pname = bits[0].trim();
						var value = bits[1]?bits[1].trim():'';						
						var dec = new Object();
						dec.pname = pname;
						
						if (pname=='transform') {
							dec.type = 'transform';
							dec.transforms = new Array();
							bits = value.split(' ');
							bits.each(function(bit) {
								var transparts = bit.split('(',2);
								var rawvalues = transparts[1].split(')',1);
								var rawvalues = rawvalues[0].split(',');
								var values = new Array();
								rawvalues.each(function(rawval) {
//									alert(rawval+'=>'+parseFloat(rawval));
									values.push({'value':parseFloat(rawval),'unit':rawval.replace(parseFloat(rawval),'')});
								});
								dec.transforms.push({'name':transparts[0],'values':values});
							});
							
							/*
								results in:
								{
									pname:'transform',
									type:'transform',
									transforms: [
										{
											name:'translate',
											values: [
												{ value:50, unit:'px'},
												{ value:100, unit:'px'}
											]
										}
									]
							*/
						} else {
							dec.type = 'valueunit';
							dec.value = parseFloat(value);
							dec.unit = value.replace(dec.value,'');
							
							//alert('pname:'+dec.pname+', value:'+dec.value+', unit:'+dec.unit);
						}
						
						declarations.push(dec);
					}
				});
				
				this.parsedFrames.push({ 'position': position, 'offset': position/100, 'declarations': declarations});
			}

		}
	}
}

function CSSAnimSelector(selectorText, keyframes, duration, timingfunction) {
	this.selectorText = selectorText;
	this.keyframes = keyframes;
	this.duration = duration;
	this.timingfunction = timingfunction;
	this.fps = 20;
	this.delay = (1/this.fps)*1000;
	
	this.apply = function(stylesheet, browsertype) {
		if (browsertype=='webkit-animation') {
			var ruleindex = findselector(stylesheet, selectorText);
			if (ruleindex==null) {
				var webkitcss = selectorText+' { -webkit-animation-name: '+keyframes.name+';  -webkit-animation-duration: '+duration+'ms;  -webkit-animation-timing-function: '+timingfunction+';  }';
				stylesheet.insertRule(webkitcss,stylesheet.cssRules.length);
			} else {
				//stylesheet.cssRules[rule]
				var rule = stylesheet.cssRules[ruleindex];
				rule.style.webkitAnimationName = keyframes.name;
				rule.style.webkitAnimationDuration = duration+'ms';
				rule.style.webkitAnimationTimingFunction = timingfunction;
			}
		} else if (browsertype=='javascript') {
			//javascript animation fallback
		}
	};
	
	this.aniStart = false;
	this.aniInterval = false;
	this.aniElements = false;
	
	this.stopAnimate = function(browsertype,self) {
		if (browsertype!='javascript') return;
		if (!self) self = this;
		
		if (self.aniInterval) {
			//TODO clear out declarations ("jump to end state")
			clearInterval(self.aniInterval);
			self.animateFrame(browsertype,self,1);
			self.aniInterval = false;
			self.aniStart = false;
			self.aniElements = false;
		}
	};

	this.startAnimate = function(browsertype) {
		if (browsertype!='javascript') return;
		this.aniElements = $$(this.selectorText);
		var self = this;
		self.aniStart = (new Date()).getTime();
		self.animateFrame(browsertype,self);
		this.aniInterval = setInterval(function() {self.animateFrame(browsertype,self)},this.delay);
	};
	
	this.animateFrame = function(browsertype,self,override) {
		var offset = ((new Date()).getTime()-self.aniStart)/self.duration;
		if (typeof override!='undefined')
			offset = override;
		
		if (offset>1) {
			self.stopAnimate(browsertype,self);
		} else {
			//first find what frames we're between
			var frames = self.keyframes.parsedFrames;
			var beginframe = null;
			var endframe = null;
			frames.each(function(frame, framei) {
				if (framei<frames.length-1 && offset>=frame.offset) {
					beginframe = frame;
					endframe = frames[framei+1];
				}
			});
			
			offset = (offset-beginframe.offset)/(endframe.offset-beginframe.offset);

			//then do the math
//			var fancyOffset = offset;	// linear
			var offset =  1 - Math.sin((1 - offset) * Math.PI / 2); //sine ease-in
//			var fancyOffset = 1- (1 - Math.sin((1 - (1-offset)) * Math.PI / 2)); //sine ease-out
			//var fancyOffset = Math.pow(offset, [4 + 2]); //quad
			
			this.aniElements.each(function(elem) {
				//TODO make out of order declarations work!!!
				beginframe.declarations.each(function(dec,deci) {
					if (dec.type=='valueunit') {
						var startval = dec.value;
						var endval = endframe.declarations[deci].value;
						var val = startval + (endval-startval)*offset;
						if (dec.pname=='z-index')
							val = Math.floor(val);
						
						if (dec.unit=='' && endframe.declarations[deci].unit!='')
							dec.unit = endframe.declarations[deci].unit;
						
						if (elem.style.setProperty)
							elem.style.setProperty(dec.pname, val+dec.unit, null);
						else
							elem.style[dec.pname] = val+dec.unit;
						
					} else if (dec.type=='transform') {
						//TODO
						var transformname = browsername+'-transform';
						var transformvalue = '';
						dec.transforms.each(function(trans, transi) {
							transformvalue += trans.name+'(';
							trans.values.each(function(val, vali) {
								var startval = val.value;
								var endval = endframe.declarations[deci].transforms[transi].values[vali].value;
								var midval = startval + (endval-startval)*offset;
								
								if (val.unit=='' && endframe.declarations[deci].transforms[transi].values[vali].unit!='')
									val.unit = endframe.declarations[deci].transforms[transi].values[vali].unit;

								transformvalue += midval+val.unit + ((vali==trans.values.length-1)?'':',');
							});
							transformvalue += ') ';
							
//							if (self.keyframes.name=='leftbouncein')
//								$('temp').innerHTML += self.selectorText+': '+transformvalue+'<br>';
						});
						
						if (elem.style.setProperty)
							elem.style.setProperty(transformname, transformvalue, null);
						else
							elem.style[transformname] = transformvalue;
					}
				});
			});
		}
	};
}
