/**
 * jquery.bobblehead.js implements a bobble-head movement
 * requires the jquery-rotate plugin
 */

jQuery.fn.rotateRight = function(angle) {
	this.rotate(angle==undefined?90:angle);
}

jQuery.fn.rotateLeft = function(angle) {
	this.rotate(angle==undefined?-90:-angle);
}

;(function($){
	var cache = [];
	$.fn.extend({
		preloadImg: function() {
			var args_len = arguments.length;
			for (var i = args_len; i--;) {
				var cacheImage = document.createElement('img');
				cacheImage.src = arguments[i];
				cache.push(cacheImage);
			}
		}

		,rotate : function(angle,whence) {
			// this is only valid for images and canvases
			if (this[0].tagName.toUpperCase() != 'IMG' && this[0].tagName.toUpperCase() != 'CANVAS')
				return this;

			var p = this;

			// we store the angle inside the image tag for persistence
			p.data('angle', true == whence ? angle : ((typeof p.data('angle') == 'undefined'?0:p.data('angle')) + angle) % 360);

			var newAngle = p.data('angle');

			var rotation = (newAngle >= 0)
				? Math.PI * newAngle / 180
				: Math.PI * (360+newAngle) / 180;

			var costheta = Math.cos(rotation);
			var sintheta = Math.sin(rotation);

			// IE just uses the CSS filter
			if (document.all && !window.opera) {
				p.css('filter',"progid:DXImageTransform.Microsoft.Matrix(M11="+costheta+",M12="+(-sintheta)+",M21="+sintheta+",M22="+costheta+",SizingMethod='auto expand')");
				return p;
			}

			// check if the element is an image or a canvas
			if(p[0].tagName.toUpperCase() == 'IMG') {
				var canvas = $(document.createElement('canvas'));
			} else {
				var canvas = p;
			}
			canvas.data('angle',p.data('angle'));
			if (!p.data('oImage')) {
				var oImage = p.clone(1);
				oImage[0].src = p[0].src;
				//_rotateCanvas(canvas,rotation,p);
			} else {
				var oImage = p.data('oImage').clone(1);
				oImage[0].src = p.data('oImage')[0].src;
				//canvas.data('oImage', oImage);
				//_rotateCanvas(canvas,rotation,p);
			}

			canvas.data('oImage', oImage);

			var oImage = canvas.data('oImage')[0];

			var oldWidth = oImage.width;
			var oldHeight = oImage.height;
			var newWidth = Math.abs(costheta * oldWidth) + Math.abs(sintheta * oldHeight);
			var newHeight = Math.abs(costheta * oldHeight) + Math.abs(sintheta * oldWidth);
			canvas[0].width = canvas[0].style.width = newWidth;
			canvas[0].height = canvas[0].style.height = newHeight;

			var context = canvas[0].getContext('2d');
			context.save();
			if (rotation <= Math.PI/2) {
				context.translate(sintheta * oldHeight,0);
			} else if (rotation <= Math.PI) {
				context.translate(newWidth,-costheta * oldHeight);
			} else if (rotation <= 1.5*Math.PI) {
				context.translate(-costheta * oldWidth , newHeight);
			} else {
				context.translate(0,-sintheta * oldWidth);
			}
			context.rotate(rotation);
			try {
				context.drawImage(oImage, 0, 0, oldWidth, oldHeight);
			} catch(e) {
				return p;
			}
			context.restore();

			if(p[0].tagName.toUpperCase() == 'IMG') {
				$(p).replaceWith(canvas);
			}
			return canvas;
		}

		,rotateAnimate: function(options) {
			var defaults = {
				'theta' : '45' // degrees
				,'speed' : '400' // milliseconds
				,'fps' : '24'
				,'callBack' : ''
				,'callBackData' : []
			};

			var options = $.extend(defaults, options);

			return this.each(function() {
				var o = options;
				var p = $(this);
				var newAngle = p.data('angle');
				newAngle = (typeof newAngle == 'undefined') ? 0 : newAngle;

				// set the angle to be rotated each frame
				var frameLen = Math.round(1000/o.fps);
				var frameCount = Math.round(o.speed / frameLen);
				var frameAngle = (o.theta - newAngle) / frameCount;
				var t = 0;

				p._excecuteRotate(o.theta, newAngle, frameAngle, t, frameLen, o.callBack, o.callBackData);
			});
		}
		,_excecuteRotate:function(theta_1, theta_2, frameAngle, timeout, frameLen, callBack, cbData) {
			if ( Math.abs(theta_1 - theta_2) < Math.abs(frameAngle) || Math.abs(frameAngle) < 1 ) {
				clearTimeout(timeout);
				var tmpFun = eval(callBack);
				if(typeof tmpFun == 'function') {
					var funCall = "tmpFun(";
					for (var ii = 0 ; ii < cbData.length ; ii++ ) {
						funCall += ii > 0 ? ',' : '';
						funCall += "cbData[" + ii + "]";
					}
					funCall += ")";
					eval(funCall);
				}
				return true;
			}
			var p = this.rotate(frameAngle);
			
			timeout = setTimeout(function() {
					p._excecuteRotate(theta_1, p.data('angle'), frameAngle, timeout, frameLen, callBack, cbData);
				}
				,frameLen
			);
			
		}
		// function used to initialize the bobbleheads
		,bobbleHead:function(options) {
			var defaults = {
				'maxTheta' : '25'
			};

			var options = $.extend(defaults, options);

			var _pullBack = function(el,o) {
				$(el).rotateAnimate({'theta':o.maxTheta,speed:'100',callBack:function(el,o) {
					_bobble(el,o);
					},callBackData:[el,o]
				});
			};

			var _shake = function(el,theta,t) {
				if (typeof t == 'undefined') t = 0;
				var newTheta = (theta)*(-1);

				$(el).rotateAnimate({'theta':newTheta,'speed':'10','fps':'60'});

				t = setTimeout(function() {
						_shake(el,newTheta,t);
					}
					,300
				);

			};

			var _bobble = function(el,o,t,stack) {
				if (typeof t == 'undefined') t = 0;
				if (typeof stack == 'undefined') stack = 5;
				var oldTheta = (typeof $(el).data('angle') != 'undefined')
					? Math.round($(el).data('angle'))
					: o.maxTheta;
				var newTheta = Math.abs(oldTheta) > 0 
					? Math.round(Math.abs(oldTheta) - 5) * (Math.abs(oldTheta)/oldTheta) * (-1)
					: 0;

				$(el).rotateAnimate({'theta':newTheta,'speed':'200'});

				stack--;
				if (newTheta == Math.round(oldTheta) && stack >= 0) {
					clearTimeout(t);
					$(el).one('mouseover',function() {
						_pullBack(this,o);
					});
				} else {
					t = setTimeout(function() {
							_bobble(el,o,t);
						}
						,300
					);
				}

			};

			var _initCanvas = function(el,o)
			{
				var el = $(el);
				
				if (el.parent().find('canvas').size() > 0) { return false; } // already exists
				
				if (document.all && !window.opera) {
					var canvas = el;
				} else {
					// preload images
					var canvas = $(document.createElement('canvas'));
					canvas.data('angle',0);
					
					// replace the image with the canvas element to be manipulated
					var oImageTmp = el.clone(1);
					oImageTmp[0].src = el.attr('src');
					canvas.data('oImage', oImageTmp);

					var oImage = canvas.data('oImage')[0];

					var oldWidth = oImage.width;
					var oldHeight = oImage.height;
					canvas[0].width = oldWidth;
					canvas[0].height = oldHeight;

					var context = canvas[0].getContext('2d');
					context.save();
					context.drawImage(oImage, 0, 0, oldWidth, oldHeight);
					context.restore();
					
					canvas.insertAfter(el);
				}
				
				canvas.one('mouseover',function() {
					return _pullBack(this,o);
				});
				
				canvas.trigger('mouseover');
				
			};

			return this.each(function() {
				var o = options;
				
				$(this).one('mouseover',function() { // mouseover of img, only execute once
					_initCanvas(this,o); // create canvas
					if (document.all && !window.opera) { /**/ } else { $(this).hide(); } // hide image after canvas created if not IE
				});				
			});
		}
	});
})(jQuery);
