/**
 * @author sigr0005
 */

/*
 * Todos:
 * 0.  Remove selector background image and use img tag instead
 * 0.1 Padding around text areas x2
 * 0.2 Remove additional referenced to div tags in selection events												X
 * 0.3 Reposition selector and content frames using js															X
 * 0.4 Adjust padding between four bottom image selections, for IE6 :-(
 * 0.5 Adjust contentFrames width using jQuery instead of writing it in
 * 0.6 Spinner seems to be stopping all animations
 * 1.  Make it work in IE6
 * 	a. z-index?
 * 2.  Add player support
 * 	a. Use <embed>/<object> combination
 * 	b. Update for HTML 5
 * 3.  Reset timeout event after mouseout event for frames														X
 * 4.  Replace button's text with images?
 * 5.  Pause when mouseover controls but do not set play status icon
 * 6.  Automatically set the hight of the content frames														X
 * 7.  Relabel tags using "spinner-..."
 * 8.  "add" function for dynamic content?
 * 	a. Reference by <li>s
 * 	b. Generate array listing from <li>s?
 * 		i. 'find' a.href within content frames (will become independent of order this way as well as static)
 * 9.  Control bar fades away if after mouseout
 * 10. Cleanup
 * 11. Add timeline that events occured for player
 * 	a. Write widget to Google analytics to show this data
 * 12. Rescope the params attribute for the creator of Spinner class
 * 13. Document functions
 * 14. Use for(var e in element) ...
 * 15. Make sure that player/frame names are unique.
 */

/**
 * Sets the parameter as spinner widget.  Adds status icon to indicate that the spinner
 * is spinning or stopped on a frame.
 * @param {String} spinnerFrame A jQuery path to the HTML DOM.
 * @param {Object} params		Parameters 'content', 'selector', 'buttons'
 */
function Spinner(spinnerFrame, params)
{
	var params				= params || {};
	// Created HTML nodes page layout			[PARAM]	- scope?					: [Default]
	var frame				= $(spinnerFrame);									// Probably don't need...
	var content				= params.content?	$(params.content)					: frame.children("div");
	var contentFrames		= content.children("div.spinner-contentFrame");
	var selector			= params.selector?	$(params.selector)					: frame.children("ul");		// ...or <ol>
	var buttons				= selector.children("li");
	buttons					= params.buttons?	buttons.children(params.buttons)	: buttons.children("a");
	var zIndexBase			= params.zIndexBase || 0;
	var playerElements		= params.players?	$(params.players)					: contentFrames.find(".spinner-swfPlayer").find("object").add("embed");
	var players				= [];
	
	// Added UIs:
	var statusNode			= $(document.createElement("a"));

	// Status variables:
	var effectIn			= {opacity: 1};
	var effectOut			= {opacity: 0};
	var effectsRate			= 2000;		// How long the effects take to transition between frames
	var frameRate			= 5000;		// How long it takes before transitioning between frames
	var interval;
	var spinning			= false;
	var previous			= 0;
	var index				= 0;
	var hash				= location.hash;
	var isSelecting			= false;			// For when the user is moused over the selection region.
	
	///////////////////////////////////////////////////////////////////////////////////////////////////////
	// Initialization:																					 //
	///////////////////////////////////////////////////////////////////////////////////////////////////////	
	init					= function()
	{
		var o	= this;		// For reseting 'this' variable
		var ps	= spinning;	// Previous State of the spinner
		
		// Remove link URL from selection buttons but keep their hash part:
		var href;
		for(var b=0; b<buttons.length; b++) if(buttons[b].href) {
			buttons[b].href	= "#" + (href = buttons[b].href.replace(/^[^#]*#/, ""));
			for(var i=0; i<contentFrames.length; i++) if(contentFrames[i].id && contentFrames[i].id === href) { buttons[b].onclick = (function(index) {
				return function(event) { updateState(undefined, index); if(event) event.preventDefault(); else window.event.returnValue = false; };
			})(i); break; }
		}
		
		// Todo: Use href's instead of a numbered index.  Problem was with next();
		//var i = 0; for each(var f in contentFrames) if(f.id) if(f.id === hash) {index = i; break;} else i++;
		index	= $(hash).prevAll("[id]").length;	// Works for now.
		
		///////////////////////////////////////////////////////////////////////////////////////////////////
		// Condense Block																				 //
		///////////////////////////////////////////////////////////////////////////////////////////////////
		// Todo: Relabel everything to appropriate CSS class names.
		// Todo: Add support for just <li>s instead of <a>s
		statusNode.addClass("spinner-button");
		statusNode.addClass("spinner-toggleButton");
		statusNode.attr("href", "");
		statusNode.click(function(event) { o.toggle(); ps = spinning; if(event) event.preventDefault(); else window.event.returnValue = false; });
		
		selector.css({padding: 10, position: "relative", zIndex: 100});
		var li	= document.createElement("li");
		li.className	= "spinner-selector-buttonFrame";
		li.appendChild(statusNode[0]);
		statusNode.text(" ");
		selector.append(li);

		///////////////////////////////////////////////////////////////////////////////////////////////////
		// Set the active frame based on the hash from location URL or start spinning:
		//	I think I forgot this part... :O
		///////////////////////////////////////////////////////////////////////////////////////////////////
		
		// Set the height of the 'spinner-frame':
		content.css({position: "relative"});			// I found the height depends on the CSS position property
		var w	= 0; //contentFrames.outerHeight();		// Stupid jQuery, I still <3 U :-D
		for(var i=0; i<contentFrames.length; i++) if(w < contentFrames.eq(i).outerHeight()) w = contentFrames.eq(i).outerHeight();
		
		var h	= selector.children().outerHeight();
		var t	= h +parseInt(selector.css("padding-top"))
					+parseInt(selector.css("padding-bottom"))
					+parseInt(selector.css("margin-top"))
					+parseInt(selector.css("margin-bottom"));
		selector.css({height: h, top: w+"px"});
		frame.css({height: (w+t)+"px"});
		content.css({width: frame.parent().width(), height: w+"px", top: (-t)+"px"});
		//content.width(frame.parent().width());
		contentFrames.height(w);
		///////////////////////////////////////////////////////////////////////////////////////////////////
		
		///////////////////////////////////////////////////////////////////////////////////////////////////
		// Events:																						 //
		///////////////////////////////////////////////////////////////////////////////////////////////////
		frame.mouseenter(function(event)	{ o.pause();	});	// Can I use 'this'?
		frame.mouseleave(function(event)	{ o.resume();	});
		selector.mouseenter(function(event)	{ isSelecting = true; o.resume(); event.stopPropagation();	});
		selector.mouseleave(function(event)	{ 
			isSelecting = false;
			event.stopPropagation();
			var node = event.relatedTarget; while(node && node !== frame[0]) node = node.parentNode;
			if(node) frame.trigger("mouseenter");
		});
		
		// Player events:
		for(var p=0; p<playerElements.length; p++) playerElements[p].onload = function() {
			players.push(this);
			this.addEventListener("PLAYING", function() { o.stop(); });
		};
		///////////////////////////////////////////////////////////////////////////////////////////////////
		
	 	if(index) this.stop(); else this.play();
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////
	// Methods:																							 //
	///////////////////////////////////////////////////////////////////////////////////////////////////////
	//	PRIVATE:																						 //
	///////////////////////////////////////////////////////////////////////////////////////////////////////
	// Todo: Add class name to button here?
	hide					= function(index) { var e = $(contentFrames[index]); e.css(effectOut); e.removeClass("selected");	};
	show					= function(index) { var e = $(contentFrames[index]); e.css(effectIn);  e.addClass("selected");		};
	next					= function() { previous = index; return (index = ++index % contentFrames.length);	};
	updateState				= function(isSpinning, newIndex) {
		///////////////////////////////////////////////////////////////////////////////////////////////////
		// Always stop animations on state changes:														 //
		///////////////////////////////////////////////////////////////////////////////////////////////////
		/*
		 * 1. Stop animations	+
		 * 2. Stop videos		+
		 */
		contentFrames.filter(":animated").not(this).stop(false, false);

		///////////////////////////////////////////////////////////////////////////////////////////////
		// Only update when there is a change.  But not if you want to stop the spinner...
		if(typeof newIndex === "number" && newIndex !== index) {
			previous	= index;					// Remove?
			index		= newIndex;
			for(var i=0; i<players.length; i++)	players[i].stop();
		}
		
		var e = $(contentFrames).not(contentFrames[index]);	e.css(effectOut); e.removeClass("selected"); e.css({display: "none",/*  left: "-999em",*/ "z-index": zIndexBase});
		var e = $(contentFrames[index]); 					e.css(effectIn);  e.addClass("selected");    e.css({display: "block",/* left: "0em",*/ "z-index": zIndexBase + contentFrames.length});
		///////////////////////////////////////////////////////////////////////////////////////////////

		///////////////////////////////////////////////////////////////////////////////////////////////////
		// update spinning state icon:																	 //
		///////////////////////////////////////////////////////////////////////////////////////////////////
		if(typeof isSpinning === "boolean" && isSpinning !== spinning) {
			// Todo: Just use .(add/remove)Class("playing")...
			statusNode[0].className	= "spinner-button spinner-toggleButton " + ((spinning = isSpinning)? "playing" : "paused");
			window.clearInterval(interval);
			if(spinning) {
				for(var i=0; i<players.length; i++)	players[i].stop();
				interval = window.setInterval(function(event) { changeContent.call(this, event); }, frameRate);
			}
		}
		///////////////////////////////////////////////////////////////////////////////////////////////////
		
		///////////////////////////////////////////////////////////////////////////////////////////////////
		// Update number icons:																			 //
		///////////////////////////////////////////////////////////////////////////////////////////////////
		$(buttons).removeClass("selected");
		$(buttons[index]).addClass("selected");		// Use .eq(index)?
		/*
		$(buttons[previous]).removeClass("selected")
		$(buttons[index]).addClass("selected");
		hide(previous);
		show(index);
		*/
		///////////////////////////////////////////////////////////////////////////////////////////////////
	};
	
	///////////////////////////////////////////////////////////////////////////////////////////////////////
	// 	The draw scene function																			 //
	///////////////////////////////////////////////////////////////////////////////////////////////////////
	changeContent			= function(event)
	{
		if(!isSelecting) {	// Implement animation:
			$(contentFrames[index]).animate(effectOut, {duration: effectsRate, queue: false});
			next();
			var e = $(contentFrames[index]); e.css(effectOut); e.css({display: "block"/* left: "0em"*/ }); e.addClass("selected");
			$(contentFrames[index]).animate(effectIn,  {duration: effectsRate, queue: false, complete: updateState});
		}
	};
	
	///////////////////////////////////////////////////////////////////////////////////////////////////////
	//	PUBLIC:																							 //
	///////////////////////////////////////////////////////////////////////////////////////////////////////
	this.play				= function() { updateState(true);	}
	this.stop				= function() { updateState(false);	}
	this.toggle				= function() { if(spinning)	this.stop(); else this.play();	};
	var ps;					// Previous State
	this.pause				= function() { ps = spinning; this.stop();	};  // Optionally, may not 'stop()' but only prevents spinner from moving
	this.resume				= function() {
		var resume	= true;
		if(typeof ps !== "undefined") for(var i=0; i<players.length; i++) if(players[i].state === "PLAYING") { resume = false; continue; }
		if(resume) { updateState(ps); ps = undefined; }
	};

	// Finish this definition by calling the init routine:
	init.call(this);
}
