/**
 * @author Rick Ogden Copyright (c) University of Salford 2010
 * @version 1.0
 */

var Fader = new Class({
    Implements: Options,
    options: {
        // Length of time (in seconds) before the transition to the next slide
        // null means there will be no automatic transition
        timer: null,    
        
        // Slide to start on (in array). -1 for a random start point
        start: 0,
        
        // Wheher it should cycle through the slides in order in the array
        // Please note, out of order will not alter the "previous" button
        // behaviour      
        inOrder: true,
        
        // How long (in seconds) the transition should take
        duration: 1,
        
        // Element that is to act as the next button.
        nextButton: null,
        
        // Element that is to act as the previous button
        previousButton: null,
        
        // Whether to pause on the current slide when the mouse hovers over
        pauseOnMouseOver: true
    },
    
    /**
     * The constructor. Sets up the elements and adds the events to the elements
     * @constructor
     * 
     * @param {Element} container The element containing the slides
     * @param {Element} elArray The array of elements to cycle through
     * @param {Object} options Other optional settings
     */
    initialize: function(container, elArray, options) {
        this.setOptions(options);
        this.container = container;
        this.elArray = elArray;
        elArray.dispose();
        elArray.setStyles({'position':'absolute'});
        if(this.options.start >= 0) { 
            this.set(this.options.start);
        } else { // decide to randomise start slide
            var rand = $random(0, elArray.length - 1);
            this.set(rand);
        }
        if(this.options.timer != null) {
            this.start(); //start the automatic transition
        }
        
        if(this.options.nextButton != null) {
            // assign events to the next button
            this.options.nextButton.addEvent('click', function(e){
                e.preventDefault();
                this.next();
            }.bind(this));
        }
        
        if(this.options.previousButton != null) { 
            // assign events to previous button
            this.options.previousButton.addEvent('click', function(e){
                e.preventDefault();
                this.previous();
            }.bind(this));
        }
        if(this.options.pauseOnMouseOver) {
            // pause on hover of element
            this.container.addEvents({'mouseenter': this.pause.bind(this),
                'mouseleave': this.resume.bind(this)});
        }
    },
    
    /**
     * Sets the delay before the next transition
     *
     * @param {number} delay The length of the delay(in milliseconds)
     */
    setDelay: function(delay) {
        $clear(this.delay); //clear whatever delay is there
        this.original = new Date().getTime() + delay; // original transition time
        this.delay = this.next.delay(delay, this); // sets the delay on the transition
    },
    
    /**
     * Starts the countdown before the next transition
     */
    start: function() {
        if(this.options.timer != null) {
            // call setDelay with specified delay
            this.setDelay(this.options.timer * 1000); 
        }
    },
    
    /**
     * Determines what the next slide is, and then trigers the transition
     */
    next: function() {
    var next;
        // decide to display next in array or a random slide
        if(this.options.inOrder) {
            next = this.current + 1;
            if(next >= this.elArray.length) next = 0;
        } else {
            do {
                next = $random(0, elArray.length - 1);
            } while(next == this.current);
        }
        // perform transition if not already in transition
        if(!this.intransition)
        this.transition(next);
    },
    
    /**
     * Determines the slide before it in the array (or at the end if it's at the
     * beginning), and triggers the transition
     */
    previous: function() {
        var prev = this.current - 1;
        if(prev < 0) prev = this.elArray.length - 1;
        if(!this.intransition)
        this.transition(prev);
    },
    
    /**
     * Will stop any transition and implement new one
     *
     * @param {int} key The array key of the element to show
     */
    jump: function(key) {
        if(this.intransition){
            this.crossfade.cancel();
        }
        this.transition(key);
    },
    
    /**
     * Transitions to the next element specified by the array key
     *
     * @param {int} key The array key of the element to show
     */
    transition: function(key) {
        this.crossfade = new Fx.Tween(this.elArray[key],{
            link: 'cancel',
            duration: this.options.duration * 1000,
            
            // When transition has finished
            onComplete: function(){
                this.elArray[this.current].dispose();
                this.intransition = false;
                this.current = key;
            }.bind(this),
            
            // If transition is cancelled
            onCancel: function(){
                this.elArray[key].dispose();
            }.bind(this),
            
            // When transition is started
            onStart: function() {
                this.intransition = true;
                this.elArray[key].setPosition(this.slidePosition);
                this.elArray[key].inject(this.container);
            }.bind(this)
        });
        this.crossfade.set('opacity', 0);
        this.crossfade.start('opacity', 1); // initialise fade
        this.start(); // execute start method
    },
    
    /**
     * Instantly sets the specified element (no transition)
     *
     * @param {int} key The array key of the element to display
     */
    set: function(key) {
        this.elArray.dispose(); // remove any injected slides
        this.elArray[key].inject(this.container); // inject new slide
        this.slidePosition = this.elArray[key].getOffsetParent();
        this.current = key; //sets the current array element key
    },
    
    /**
     * Stops the automation
     */
    stop: function() {
    if(this.intransition) this.crossfade.cancel();
        $clear(this.delay);
    },
    
    /**
     * Pauses the automation to be resumed later from the same state.
     * It will not stop mid transition.
     */
    pause: function() {
        this.delayPos = this.original - new Date().getTime();
        $clear(this.delay);
    },
    
    /**
     * Resumes from paused state.
     */
    resume: function() {
        this.setDelay(this.delayPos);
    }
});

