/*
 * Slideshow - Creates a slideshow
 *
 * Syntax:
 *
 * var slideshow = new Slideshow([options]);
 *
 * Arguments:
 *
 * options - (object: optional) An object with options for the slideshow. See below.
 *
 * Options:
 *
 * container - (mixed: defaults to element id 'slideshow') The element where the slideshow must be applied to.
 * elements - (mixed: defaults to 'img') The elements that will become the slides.
 * wait - (number: defaults to 4000) The timeout before the next element is faded in.
 * duration - (number: defaults to 1000) The duration of the effect in ms. Can also be one of: 'short' - 250ms, 'normal' - 500ms, 'long' - 1000ms.
 * transition - (function: defaults to Fx.Transitions.Quart.easeIn) The equation to use for the effect, see Fx.Transitions.
 * autoStart - (boolean: defaults to true) Start the slideshow automatically or wait for the user to call start().
 * useCookie - (mixed: defaults to false) Starts the slideshow where it previously stopped.
 * onStart - (function) Executes when the slideshow starts.
 * onChange - (function) Executes when the transition to a next elements is complete.
 * onStop - (function) Executes when the slideshow is stopped.
 *
 * Functions:
 *
 * playing() - Returns true if slideshow is playing, false if not.
 * start() - Start the slideshow.
 * stop() - Stop the slideshow.
 * next() - Fade to the next element.
 * previous() - Fade to the previous element.
 * go(n, skipAnimation) - Go to the nth element (with or without fading).
 */

var Slideshow = new Class({
    Implements: [Options, Events],

    options: {
        container: 'slideshow',
        elements: 'img',
        wait: 4000,
        duration: 1000,
        transition: Fx.Transitions.Quart.easeOut,
        autoStart: true,
        useCookie: false,
        onStart: $empty,
        onChange: $empty,
        onStop: $empty
    },

    container: null,
    elements: null,
    index: 0,
    intervalId: -1,

    initialize: function(options) {
        // Merge options
        this.setOptions(options);

        // Get container and validate
        this.container = $(this.options.container);
        if (this.container == null) {
            return;
        }

        // Get all elements and validate
        this.elements = $$(this.container.getElements(this.options.elements));
        if (this.elements.length <= 1) {
            return;
        }

        // Set style for the container
        this.container.setStyle('position', 'relative');

        // Set effects for all elements
        this.elements.set('tween', {
            duration: this.options.duration,
            transition: this.options.transition
        });

        // Set styles for all elements
        this.elements[0].setStyles({
            opacity: 1
        });
        for (var i = 1; i < this.elements.length; i++) {
            this.elements[i].setStyles({
                position: 'absolute',
                top: this.container.getStyle('padding-top').toInt() || 0,
                left: this.container.getStyle('padding-left').toInt() || 0,
                zIndex: 0,
                opacity: 0
            });
        }

        this.container.setStyles({
            width: this.elements[0].getSize().x,
            height: this.elements[0].getSize().y
        });

        // Start slideshow if autoStart = true
        if (this.options.autoStart) {
            this.start();
        }
    },

    playing: function() {
        return this.intervalId >= 0;
    },

    start: function() {
        // Only start if the slideshow isn't started yet
        if (!this.playing()) {
            this.fireEvent('start', this.index);

            // Check cookie
            if (this.options.useCookie) {
                var cookieIndex = Cookie.read(this.options.useCookie.key) || 0;
                this.go(cookieIndex, true);
            }

            this.intervalId = this.next.periodical(this.options.wait, this);
        }
    },

    stop: function() {
        // If the slideshow is started, stop it and reset variable
        if (this.playing()) {
            $clear(this.intervalId);
            this.intervalId = -1;
            this.fireEvent('stop');
        }
    },

    next: function() {
        this.go.call(this, this.index + 1);
    },

    previous: function() {
        this.go.call(this, this.index - 1);
    },

    go: function(n, skipAnimation) {
        // Save current index
        var previousIndex = this.index;

        // Set new index
        var nextIndex = n;
        if (nextIndex < 0) {
            nextIndex = this.elements.length - 1;
        } else if (n >= this.elements.length) {
            nextIndex = 0;
        }

        if (previousIndex != nextIndex) {
            // Cancel all running effects
            this.elements.get('tween').each(function(tween) {
                tween.cancel();
            });

            // Set current element on top
            this.elements[previousIndex].setStyles({
                opacity: 1,
                zIndex: 2
            });

            // Set next element beneath current element 
            this.elements[nextIndex].setStyles({
                opacity: 1,
                zIndex: 1
            });

            // Set other elements beneath current and next element
            this.elements.filter(function(element, index) {
                return index != previousIndex && index != nextIndex;
            }).setStyles({
                opacity: 0,
                zIndex: 0
            });

            // Fade out current element
            if (!skipAnimation) {
                this.elements[previousIndex].fade(0);
            } else {
                this.elements[previousIndex].setStyle('opacity', 0);
            }

            // Set index
            this.index = nextIndex;

            // Fire change event
            this.fireEvent('change', [previousIndex, nextIndex]);

            // Store current item in cookie
            if (this.options.useCookie) {
                Cookie.write(this.options.useCookie.key, this.index, this.options.useCookie.options || {});
            }
        }
    }
});

/*
 * Slideshow.Controlled - Creates a slideshow with controls
 *
 * Syntax:
 *
 * var slideshow = new Slideshow.Controlled([options]);
 *
 * Arguments:
 *
 * options - (object: optional) The Slideshow options object, plus the options described below:
 *
 * Options:
 *
 * controls - (object: defaults to { previous: true, index: true, next: true }) The controls to create (span elements).
 * controlsContainer - (element: defaults to a div with class 'controls') The container the controls are injected to.
 * autoContinue - (boolean: defaults to true) Start playing the slideshow after manual navigation.
 */
Slideshow.Controlled = new Class({
	Extends: Slideshow,
	
	options: {
		controls: {
			previous: true,
			index: true,
			next: true
		},
		controlsContainer: new Element('div', {
			'class': 'controls'
		}),
		autoContinue: true
	},
	
	controls: {
		previous: null,
		index: [],
		next: null
	},
	controlsContainer: null,
	
	initialize: function(options){
		this.setOptions(options);
		
		// Delay autostart
		var autoStart = this.options.autoStart;
		this.options.autoStart = false;
		
		// Set up slideshow
		this.parent(this.options);

        // Validate elements
		if (this.elements.length <= 1) {
		    return;
		}
		
		// Get controlsContainer
		this.controlsContainer = $(this.options.controlsContainer).addEvents({
			'mousedown': $lambda(false),
			'selectstart': $lambda(false)
		});
		
		// Add controls
		if (this.controlsContainer != null){
			// Add previous control
			if (this.options.controls.previous){
				this.controls.previous = new Element('span', {
					'class': 'previous',
					'html': '<strong>&lt;</strong>',
					'events': {
						'click': function(){
							var playing = this.playing();
							this.stop();
							this.previous();
							if (this.options.autoContinue && playing){
								this.start();
							}
						}.bind(this)
					}
				}).inject(this.controlsContainer);
			}
			
			// Add all index controls
			if (this.options.controls.index){
				this.elements.each(function(element, index){
					this.controls.index[index] = new Element('span', {
						'class': 'index',
						'text': index + 1,
						'events': {
							'click': function(){
								var playing = this.playing();
								this.stop();
								this.go(index);
								if (this.options.autoContinue && playing){
									this.start();
								}
							}.bind(this)
						}
					}).inject(this.controlsContainer);
					
					if (this.index == index){
						this.controls.index[index].addClass('active');
					}
				}, this);
			}
			
			// Add next control
			if (this.options.controls.next){
				this.controls.next = new Element('span', {
					'class': 'next',
					'html': '<strong>&gt;</strong>',
					'events': {
						'click': function(){
							var playing = this.playing();
							this.stop();
							this.next();
							if (this.options.autoContinue && playing){
								this.start();
							}
						}.bind(this)
					}
				}).inject(this.controlsContainer);
			}
			
			// Add change event
			this.addEvent('change', function(currentIndex, nextIndex){
				this.controls.index[currentIndex].removeClass('active');
				this.controls.index[nextIndex].addClass('active');
			});
			
			// Inject controlsContainer to container
			this.controlsContainer.inject(this.container);
		}
		
		// Delayed autostart
		this.options.autoStart = autoStart;
		if (this.options.autoStart){
			this.start();
		}
	}
});

