/*
 * jQuery Slideshow
 * Version: .5 (14-July-2011)
 * Requires: jQuery v1.5, jquery.easing.1.3
 */
(function($){
	var Slideshow = function(element, options)
	{
		// Public static constant
		this.COMPLETE = "complete";
		this.CHANGE = "change";
		
		// Public varibles
		this.currentSlide = null;

		// Private varibles
		var slideshowClass = this;
		var $slideshow = $(element);
		var $slider = $('#slideshow-slider', $slideshow);
		var sliderChildren = $('.slideshow-slide', $slider);
		var sliderWidth = 0;
		var slideWidth = 0;
		var timer = null;
		var timeout = 5000;
		var savedTimeout;
		var currentSlideNum = 0;
		var totalSlide = sliderChildren.length;
		
		// Subscribers to the model's events. 
		var listeners = {};

		$slideshow.css({overflow : 'hidden'});
		$slider.css('float', 'none');

		if (timer) clearTimeout(timer);
		
		// Get the sum of all the slides' width.
		sliderChildren.each(function()
		{
			var $this = $(this);
			// Combine all the width of the child of #slideshow-slider div.
			slideWidth = $this.width();
			sliderWidth += slideWidth;
			$slider.css({width : sliderWidth});
		});
		
		// Mouse hover and out
		$slider.hover(function()
		{
			clearTimeout(timer);
			savedTimeout = timeout;
			timeout = 0;
		}, 
		function()
		{
			timeout = savedTimeout;
			checkTimer();
		});
		
		// Fire away!!!
		checkTimer();
		
		var onAnimationComplete = function()
		{
			// Call the complete handler.
			dispatchEvent('complete');
			
			// re-init.
			// console.log('current slide: ' + currentSlideNum);
			// If it finished the round and returned to the first slide, then exit.
			if (currentSlideNum == 0)
			{
				timeout = 0;
				return;
			}
			
			checkTimer();
		};
		
		// Start the countdown if the timeout is not zero.
		function checkTimer()
		{
			if (timeout)
			{
				timer = setTimeout(function(){
					// Check if the current slide is the last slide. If so, set back to 0.
					currentSlideNum = currentSlideNum == (totalSlide - 1) ? 0 : currentSlideNum + 1;

					slideshowClass.gotoSlide(currentSlideNum);
				}, timeout);
			}
		}
		
		/*
		 * Update the slide the current slide index.
		 * Animate the slider to the calculated position.
		 */
		function update(slide)
		{
			currentSlideNum = Number(slide);
			
			var pos = slideWidth * currentSlideNum;
			
			dispatchEvent('change');
			
			$slider.animate({left:-pos},{duration:1000,queue:false,easing:"easeInOutExpo",complete:onAnimationComplete});
		}
		
		/*
		 * Registers an event listener object.
		 * 
		 * @param type String The type of event
		 * @param fn Function The listener function that processes the event.
		 */
		this.addEventListener = function(type, fn)
		{
			type = type || 'any';
			if (typeof listeners[type] === "undefined")
			{
				listeners[type] = [];
			}

			listeners[type].push(fn);
		}
		
		/*
		 * Remove a listener from the dispatcher object.
		 * 
		 * @param type - The type of event.
		 * @param fn - The listener to remove.
		 */
		this.removeEventListener = function (type, fn)
		{
			new visitSubscribers('unsubscribe', fn, type);
		}
		
		/*
		 * Dispatches event to any subscriber.
		 * 
		 * @param type - The type of event.
		 */
		var dispatchEvent = function(type, listener)
		{
			new visitSubscribers('publish', listener, type);
		};
		
		/*
		 * Process subscribers and determine their action.
		 * 
		 * @param action - publish or unsubcribe.
		 * @param arg
		 * @param type
		 */
		var visitSubscribers = function (action, arg, type)
		{
			var pubtype = type || 'any', myListeners = listeners[pubtype];
//			console.log('pubtype : ' + pubtype);
			
			// If there's are subcribers
			if (myListeners)
			{
				var max = myListeners.length;
					
				for (var i = 0; i < max; i++)
				{
					if (action === 'publish') 
					{
						myListeners[i](arg);
					}
					else
					{
						if (myListeners[i] === arg)
						{
							myListeners.splice(i, 1);
						}
					}
				}
			}
		};

		/*
		 * Send the slideshow to the specified slide.
		 *
		 * @param slide The slide number to with the slideshow is sent.
		 */
		this.gotoSlide = function(slide)
		{
			update(slide);
		}
		
		/*
		 * Send the slideshow to the specified slide and stop the interval.
		 * 
		 * @param slide The slide number.
		 */
		this.gotoAndStop = function(slide)
		{
			clearTimeout(timer);
			timeout = 0;
			
			update(slide);
		}
		
		this.getCurrentSlideNum = function() { return currentSlideNum; }
	};

	$.fn.slideshow = function(options)
	{
        return this.each(function(key, value)
	   {
            var element = $(this);
            // Return early if this element already has a plugin instance
            if (element.data('slideshow')) return element.data('slideshow');
            // Pass options to plugin constructor
            var slideshow = new Slideshow(this, options);
            // Store plugin object in this element's data
            element.data('slideshow', slideshow);
        });
	};

})(jQuery);

