/**
 * Copyright (c) 2006, Bill W. Scott
 * All rights reserved.
 *
 * This work is licensed under the Creative Commons Attribution 2.5 License. To view a copy
 * of this license, visit http://creativecommons.org/licenses/by/2.5/ or send a letter to
 * Creative Commons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA.
 *
 * This work was created by Bill Scott (billwscott.com, looksgoodworkswell.com).
 *
 * The only attribution I require is to keep this notice of copyright & license
 * in this original source file.
 *
 * Version 0.3.4 - 11.28.2006
 *
 */
YAHOO.namespace("extension");

/**
* @class
* The slideshow class manages a set of panels
* @param {object|string} slideshowElementID The element ID (id name or id object) of the DIV that will become a slideshow
* @param {object} slideshowCfg The configuration object literal containing the configuration that should be set for this module. See configuration documentation for more details.
* @constructor
*/
YAHOO.extension.Slideshow = function(slideshowElementID, slideshowCfg) {
 		this.init(slideshowElementID, slideshowCfg);
	};

YAHOO.extension.Slideshow.prototype = {

	/**
	 * Initializes the slideshow object and all of its local members.
     * @param {object|string} slideshowElementID The element ID (id name or id object)
     * of the DIV that will become a Slideshow
     * @param {object} slideshowCfg The configuration object literal containing the
     * configuration that should be set for this module. See configuration documentation for more details.
	 */
	init: function(slideshowElementID, slideshowCfg) {
		var oThis = this;

 		this.slideshowElemID = slideshowElementID;
 		this.slideshowElem = YAHOO.util.Dom.get(slideshowElementID);

 		// Create the config object
 		this.cfg = new YAHOO.util.Config(this);

		this.cfg.addProperty("overlayOpacity", {
				value:0.7,
				suppressEvent:true
		} );

		/**
		 * firstVisible property.
		 * Sets which item should be the first visible item in the carousel. Use to set which item will
		 * display as the first element when the carousel is first displayed. After the carousel is created,
		 * you can manipulate which item is the first visible by using the moveTo() or scrollTo() convenience
		 * methods.
		 */
		this.cfg.addProperty("firstVisible", {
				value:1,
				suppressEvent:true
		} );

		/**
		 * animationSpeed property.
		 * The time (in seconds) it takes to complete the scroll animation.
		 * If set to 0, animated transitions are turned off and the new page of content is
		 * moved immdediately into place.
		 */
		this.cfg.addProperty("animationSpeed", {
				value:0.25,
				suppressEvent:true
		} );

		/**
		 * animationMethod property.
		 * The <a href="http://developer.yahoo.com/yui/docs/animation/YAHOO.util.Easing.html">YAHOO.util.Easing</a>
		 * method.
		 */
		this.cfg.addProperty("animationMethod", {
				value:  YAHOO.util.Easing.easeOut,
				suppressEvent:true
		} );

		/**
		 * animationCompleteHandler property.
		 * JavaScript function that is called when the Carousel finishes animation
		 * after a next or previous nagivation.
		 * Only invoked if animationSpeed > 0.
		 * Two parameters are passed: type (set to 'onAnimationComplete') and
		 * args array (args[0] = direction [either: 'next' or 'previous']).
		 */
		this.cfg.addProperty("animationCompleteHandler", {
				value:null,
				suppressEvent:true
		} );

		/**
		 * autoPlay property.
		 * Specifies how many milliseconds to periodically auto scroll the content.
		 * If set to 0 (default) then autoPlay is turned off.
		 * If the user interacts by clicking left or right navigation, autoPlay is turned off.
		 * You can restart autoPlay by calling the <em>startAutoPlay()</em>.
		 * If you externally control navigation (with your own event handlers)
		 * then you may want to turn off the autoPlay by calling<em>stopAutoPlay()</em>
		 */
		this.cfg.addProperty("autoPlay", {
				value:0,
				suppressEvent:true
		} );

		/**
		 * initialDelay property.
		 * Specifies how many milliseconds to wait before starting autoplay for the first time.
		 * If set to 0 (default) then autoplay starts immediately
		 */
		this.cfg.addProperty("initialDelay", {
				value:0,
				suppressEvent:true
		} );


		/**
		 * loadInitHandler property.
		 * JavaScript function that is called when the Carousel needs to load
		 * the initial set of visible items. Two parameters are passed:
		 * type (set to 'onLoadInit') and an argument array (args[0] = start index, args[1] = last index).
		 */
		this.cfg.addProperty("loadInitHandler", {
				value:null,
				suppressEvent:true
		} );

 		if(slideshowCfg) {
 			this.cfg.applyConfig(slideshowCfg);
 		}

 		this.animSpeed = this.cfg.getProperty("animationSpeed");
		this.initHandler = this.cfg.getProperty("loadInitHandler");
		this.animationCompleteHandler = this.cfg.getProperty("animationCompleteHandler");
		this.animationMethod = this.cfg.getProperty("animationMethod");
		this.autoPlay = this.cfg.getProperty("autoPlay");
		this.firstVisible = this.cfg.getProperty("firstVisible");
		this.overlayOpacity = this.cfg.getProperty("overlayOpacity");
		this.initialDelay = this.cfg.getProperty("initialDelay");
		this.autoPlayTimer = null;
		this.currSlideIdx = 0;
		this.lastSlideIdx = -1;

 		// prefetch the content class that contains the panels
		this.container = YAHOO.util.Dom.getElementsByClassName("yui-content",
												"div", this.slideshowElem)[0];

		// Get all the DIV kids, set all but first to have opacity set to 0
		this.slides = [];
		var kids = this.container.childNodes;
		for(var i=0; i<kids.length; i++) {
			var kid = kids[i];
			if(typeof(kid.tagName) !== "undefined" && kid.tagName !== "" && kid.tagName.toLowerCase() === "div") {
				this.slides.push(kid);
			}
		}

		// This is a test
		this.overlay = YAHOO.util.Dom.getElementsByClassName("yui-overlay",
												"div", this.slideshowElem)[0];

		var overlayKids = YAHOO.util.Dom.getElementsBy( function(elem) {
			// if this div is directly below the overlay then choose it
			//debugger;
			if(elem.parentNode == oThis.overlay) return true;

			// otherwise, skip
			return false;
		} ,"div", this.overlay );

		// Place the overlay directly over the container
		YAHOO.util.Dom.setX(this.overlay, YAHOO.util.Dom.getX(this.container));
		this.currOverlay = null;

		if(overlayKids.length > 0 && this.slides.length > 0) {
			this.mouseOutside = true;
			YAHOO.util.Event.addListener(document, "mousemove", function(e) {
				if(oThis.isMouseInElem(e, oThis.slides[0])) {
					// if last time we were outside the component
					if (oThis.mouseOutside) {
						// Mouse Enter

						// find corresponding overlay
						// if only one kid, then map to all of them
						oThis.currOverlay = overlayKids[oThis.currSlideIdx];

						oThis.mouseOutside = false;
						oThis.stopAutoPlay();

						YAHOO.util.Dom.setStyle(oThis.currOverlay, "visibility", "visible");
						YAHOO.util.Dom.setStyle(oThis.currOverlay, "opacity", oThis.overlayOpacity);
						YAHOO.util.Dom.setStyle(oThis.currOverlay, "zIndex", "3");
					}
				// outside our component
				} else {
					// last time we were inside the component
					if (!oThis.mouseOutside) {
						// Mouse Out
						//oThis.debugMsg("MOUSEOUT");
						oThis.mouseOutside = true;
						oThis.startAutoPlay();
						oThis.overlayShown = false;

						if(oThis.currOverlay !== null) {
							YAHOO.util.Dom.setStyle(oThis.currOverlay, "visibility", "hidden");
							YAHOO.util.Dom.setStyle(oThis.currOverlay, "opacity", "0.0");
						}
					}
				}

			});
		}

		// Wire up the various event handlers that they might have supplied
		if(this._isValidObj(this.initHandler)) {
			this.loadInitialEvt = new YAHOO.util.CustomEvent("onLoadInit", this);
			this.loadInitialEvt.subscribe(this.initHandler, this);
		}

		// get the auto play kicked off, noting any initial delay
		if(this.autoPlay !== 0) {
			if(this.initialDelay > 0) {
				var oThis = this;
				setTimeout(function() {
					oThis.autoPlayTimer = oThis.startAutoPlay();
				}, this.initialDelay);
			} else {
				this.autoPlayTimer = this.startAutoPlay();
			}
		}

	},

	isMouseInElem: function(evt, elem) {
		//var elem = YAHOO.util.Event.getTarget(evt);
		var elemReg = YAHOO.util.Dom.getRegion(elem);
		var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
		var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
		var x = evt.clientX+scrollX;
		var y = evt.clientY+scrollY;

		var mouseReg = new YAHOO.util.Region(y, x, y+1, x+1);
		// inside our component
		return elemReg.contains(mouseReg);
	},

	// /////////////////// Public API //////////////////////////////////////////
	/**
	 * Shows slide (fades previous slide, fades in new slide).
	 */
	showNextSlide: function() {
		// If mouse is inside then just skip
/*		if(!this.mouseOutside)
			return;
*/
		this.lastSlideIdx = this.currSlideIdx;

		this.currSlideIdx++;
		this.currSlideIdx = (this.currSlideIdx >= this.slides.length) ? 0 : this.currSlideIdx;

		var currSlide = this.slides[this.currSlideIdx];
		var lastSlide = this.slides[this.lastSlideIdx];

		YAHOO.util.Dom.setStyle(currSlide, "visibility", "visible");
		YAHOO.util.Dom.setStyle(currSlide, "zIndex", "2");

		if(this.animSpeed) {
			this.fadeIn = new YAHOO.util.Anim(currSlide,
										{ opacity: { to: 0.99 } },
										this.animSpeed,
										this.animationMethod);
			this.fadeOut = new YAHOO.util.Anim(lastSlide,
										{ opacity: { to: 0 } },
										this.animSpeed,
				 						this.animationMethod);
			this.fadeOut.onComplete.subscribe(this._handleAnimationComplete, [this]);

			this.fadeOut.animate();
			this.fadeIn.animate();
		} else {
			// show one, hide the other
			YAHOO.util.Dom.setStyle(currSlide, "opacity", "0.99");

			YAHOO.util.Dom.setStyle(lastSlide, "opacity", "0.0");
			YAHOO.util.Dom.setStyle(lastSlide, "visibility", "hidden");
			YAHOO.util.Dom.setStyle(lastSlide, "zIndex", "1");

			// Kick it off again (if animated, this happens at end of animation sequence)
			slideshow.autoPlayTimer = null;
			if(slideshow.autoPlay !== 0) {
				slideshow.autoPlayTimer = slideshow.startAutoPlay();
			}
		}

	},

	/**
	 * Starts up autoplay. If autoPlay has been stopped (by calling stopAutoPlay or by user interaction),
	 * you can start it back up by using this method.
	 * @param {number}	interval	optional parameter that sets the interval
	 * for auto play the next time that autoplay fires.
	 */
	startAutoPlay: function(interval) {
		// if interval is passed as arg, then set autoPlay to this interval.
		if(interval) {
			this.autoPlay = interval;
		}

		// if we already are playing, then do nothing.
		if(this.autoPlayTimer) {
			return this.autoPlayTimer;
		}

		var oThis = this;

		this.autoPlayTimer = setTimeout( function() { oThis.showNextSlide(); }, this.autoPlay );
		return this.autoPlayTimer;
	},

	/**
	 * Stops autoplay. Useful for when you want to control what events will stop the autoplay feature.
	 * Call <em>startAutoPlay()</em> to restart autoplay.
	 */
	stopAutoPlay: function() {
		if (this.autoPlayTimer !== null) {
			clearTimeout(this.autoPlayTimer);
			this.autoPlayTimer = null;
		}
	},


	// /////////////////// PRIVATE API //////////////////////////////////////////

	_isValidObj: function(obj) {

		if (null == obj) {
			return false;
		}
		if ("undefined" == typeof(obj) ) {
			return false;
		}
		return true;

	},

	_handleAnimationComplete: function(type, args, argList) {

		var slideshow = argList[0];
		var slide = slideshow.slides[slideshow.lastSlideIdx];
		YAHOO.util.Dom.setStyle(slide, "visibility", "hidden");
		YAHOO.util.Dom.setStyle(slide, "zIndex", "1");
		slideshow.autoPlayTimer = null;
		if(slideshow.autoPlay !== 0) {
			slideshow.autoPlayTimer = slideshow.startAutoPlay();
		}

		//slideshow.animationCompleteEvt.fire();
	},
	debugMsg: function(msg)
	{
		var debugArea = YAHOO.util.Dom.get("debug-area");
		if(!debugArea) {
			debugArea = document.createElement("div");
			debugArea.id = "debug-area";
			document.body.appendChild(debugArea);
		}
		debugArea.innerHTML = debugArea.innerHTML + "<br/>" + msg;
	},
	clearDebug: function()
	{
		var debugArea = document.getElementById("debug-area");
		if(debugArea) {
			debugArea.innerHTML = "";
		}
	}


};
