//
// Copyright (c) 2008 Robarov (http://www.robarov.be)
// Loosely based on http://www.xyberneticos.com/index.php/2007/10/31/prototype-carousel-class/
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// 

var RobaSlideshow = Class.create();
RobaSlideshow.prototype = {
	// Constructor
	initialize: function(slideElemID) {
		this.slideElemID = slideElemID;
		
		this.options = Object.extend({
			timeOut:			5,
			direction:			"Y",
			slideClassName:		"slide",
			prevElementID:		false,
			nextElementID:		false,
			autoSlide:			true,
			animParameters:      { duration:1 }
		}, arguments[1] || {});

		this.animRunning = "none";

		// Add a call to the function _animDone, after an effect finishes.
		Object.extend(this.options.animParameters, {afterFinish:  this._animDone.bind(this), queue: { position:'end', scope: 'robaslideshow' }});

		// Get DOM div element
		this.slideList = $(this.slideElemID).select('.slide');
		this.size = this.slideList.length;

		// Event bindings
		if(this.options.prevElementID) {
			this.prevSlide = this._clickPrev.bindAsEventListener(this);
			Event.observe(this.options.prevElementID, "click", this.prevSlide);
		}
		if(this.options.nextElementID) {
			this.nextSlide = this._clickNext.bindAsEventListener(this);
			Event.observe(this.options.nextElementID, "click", this.nextSlide);
		}
		this.onFailure  = this._onFailure.bindAsEventListener(this);
		
		// If autoslide, stop autoslide, when hovering, start it again when blurred
		if(this.options.autoSlide && this.size > 1) {
			this.mouseOut = this._mouseOut.bindAsEventListener(this);
			Event.observe(this.slideElemID, "mouseout", this.mouseOut);
		
			this.mouseOver = this._mouseOver.bindAsEventListener(this);
			Event.observe(this.slideElemID, "mouseover", this.mouseOver);
		}
		
		// If there are only 2 slides, clone them, and add them to array
		if(this.size == 2) {
			this.slideList.each(function(el) {
				var foo = el.cloneNode(true);
				el.parentNode.insert(foo, 'bottom');
			});
	
			// Refill array of slides, recalculate length
			this.slideList = $(this.slideElemID).select('.slide');
			this.size = this.slideList.length;
		}
		
		// Set slides to visible (that were hidden with css, to prevent flicker on page load)
		zCounter = parseInt($(this.slideElemID).getStyle('zIndex')) + this.size;
		this.slideList.each(function(el) {
			el.setStyle({
				top: 0,
				display: 'block',
				zIndex: zCounter
			});
			zCounter--;
		});

		// Init data
		this._init();
	},

	// Destructor
	destroy: function() {
		if(this.options.prevElementID)
			Event.stopObserving(this.options.prevElementID, "click", this.prevSlide);
		if(this.options.nextElementID)
			Event.stopObserving(this.options.nextElementID, "click", this.nextSlide);
		if(this.options.autoSlide && this.size > 1) {
			Event.stopObserving(this.slideElemID, "mouseout", this.mouseOut);
			Event.stopObserving(this.slideElemID, "mouseover", this.mouseOver);
		}
		
	},
	
	/* "Private" functions */
	_init: function() {
		this.currentIndex = 0;

		// If there is only 1 slide, hide prev/next controls, and do not init the rest of the script
		if(this.size <= 1) {
			$(this.options.prevElementID).hide();
			$(this.options.nextElementID).hide();
		} else {
			this._orderSlides();

			// if autoSlide is true, start the sliding madness automatically!
			if(this.options.autoSlide && this.size > 1)
				this.PE = new PeriodicalExecuter(function(){this._nextSlide(this);}.bind(this), this.options.timeOut);
		}
	},

	_mouseOut: function(event) {
		if(this.options.autoSlide && this.size > 1)
			this.PE = new PeriodicalExecuter(function(){this._nextSlide(this);}.bind(this), this.options.timeOut);
	},
	
	_mouseOver: function(event) {
		if(this.options.autoSlide && this.size > 1)
			this.PE.stop();
	},

	_clickNext: function(event) {
		event.stop();	 // Prevents going to # after clicking link
		this._nextSlide();
	},
	
	_nextSlide: function(event) {
		if (this.animRunning != 'none')
			return false;

		this._slide('next');
		
		return false;
	},
	
	_clickPrev: function(event) {
		event.stop();	 // Prevents going to # after clicking link
		this._prevSlide();
	},
	
	_prevSlide: function(event) {
		if (this.animRunning != 'none')
			return false;

		this._slide('prev');

		return false;
	},
	
	_onFailure: function(originalRequest){    
		this.requestIsRunning = false;
	},

	_animDone: function(event){ 
		// Set next slide as current slide;	
		if(this.animRunning == 'next') {
			this.currentIndex = (this.currentIndex + 1) % this.size;
		} else if(this.animRunning == 'prev') {
			this.currentIndex = this.currentIndex - 1;
			if(this.currentIndex < 0) {
				this.currentIndex = this.size - 1;
			}
		}
		
		this._orderSlides();
		
		this.animRunning = 'none';
			
		return;
	},
	
	/*
		Reorder slides: 
		put the previous slide left from the current slide, 
		put the next slide right from the current slide
	*/
	_orderSlides: function(event) {	
		// Prev slide
		var prevIndex = (this.currentIndex - 1);
		if(prevIndex < 0) {
			prevIndex = this.size - 1;
		}
		this.slideList[prevIndex].setStyle({
			left: (0 - this.slideList[prevIndex].getWidth()) + 'px'
		});
		// Next slide
		var nextIndex = (this.currentIndex + 1) % this.size;
		this.slideList[nextIndex].setStyle({
			left: (this.slideList[this.currentIndex].getWidth()) + 'px'
		});

		return;
	},

	_slide: function(direction) {
		this.animRunning = direction;
			
		var pxToMove = this.slideList[this.currentIndex].getWidth();
		if(direction == 'prev') {
			pxToMove = 0 - pxToMove;
		} else if(this.animRunning == 'none') {
			pxToMove = 0;	
		}
		
		// Make an array of move effects for every slide, so they can run in parallel
		// Let _animDone handle all afterFinish events, so we can call everything in the class
		var arrEffects = new Array();
		for(var i = 0; i < this.size; i++) {
			arrEffects[i] = new Effect.MoveBy(this.slideList[i], 0, (0 - pxToMove), {sync: true});
		}
		new Effect.Parallel(arrEffects, this.options.animParameters);
	}
}