//Builds a carousel model
//License: This file is entirely BSD licensed.
//Author: Brian R Miedlar (c) 2004-2009 miedlar.com
//Dependencies: prototype.js
// MODIFIED by Grégory Saive (@eVias) for Maximiles.com
// by Elma Ingénierie
// CHANGES of the fork:
// - allow automatic sliding.
// - navigation buttons are not required anymore
// - scroll is done until the last index is active
// BUGFIX :
// - when sliding to the right (next()), activating
//   starts with the first elements again when finished
//   one round.
//   BUG HISTORY:
//   - When it reached the end, activate() wouldn't fire the Activating
//     trigger, it would only scroll back and 'return;', so only the
//     scroll effect was loaded after that, no more activeItem were
//     set anymore.
// - when using scrollToIndex(), this.activate is called on the new index
//   BUG HISTORY
//   - scrollToIndex won't activate item if fireCarouselAtIndex is not declare
//
// USE of fork:
// - next() | previous() should be triggered automatically since
//   there will not be any click-events

Element.display = function(element, show) {
    Element[(show) ? 'show': 'hide'](element);
}

var CarouselItem = Class.create();
CarouselItem.prototype = {
    initialize: function() {
        this.key = null;
        this.value = null;
        this.element = null;
    }
};
var Carousel = Class.create();
Carousel.prototype = {
	debugVar : 0,
    countDone : 0,
    startActiveIndex : 0,
    leftStyleData : new Array(),
    isFirstTime : false,
    initialize: function(key, carouselElement, itemWidth, itemHeight, observer, options) {
        this.loaded = false;
        this.key = key;
        this.observer = observer;
        this.carouselElement = $(carouselElement);
        if (!this.carouselElement) { alert('Warning: Invalid carousel element: ' + carouselElement); return; }
        this.itemsElement = this.carouselElement.down('.items');
        if (!this.itemsElement) { alert('Warning: Class \'items\' does not exist as a child element in carousel: ' + carouselElement); return; }
        this.items = [];
        this.activeItem = null;
        this.activeIndex = 0;
        this.navScrollIndex = 0;
        this.itemHeight = itemHeight;
        this.itemWidth = itemWidth;
        if (!options) options = {};
        this.options = Object.extend({
            duration: 1.0,
            direction: 'horizontal',
            moveOpacity: .6,
            setSize: 4,
            allowAutoLoopOnSet: false,
            allowAutoLoopOnIndividual: true
        }, options);
        this.leftStyleData = new Array();
        this.isFirstTime = true;
        this.backElement = this.carouselElement.down('.navButton.previous');
        this.forwardElement = this.carouselElement.down('.navButton.next');
        if (this.backElement) Event.observe(this.backElement, 'click', this.scrollBackAndStop.bind(this));
        if (this.forwardElement) Event.observe(this.forwardElement, 'click', this.scrollForwardAndStop.bind(this));
    },
    load: function() {
        var eList = this.itemsElement;
        this.items.clear();

        eList.select('.item').each(function(item) {
            item.carouselKey = null;
            var sKey = '';
            try {
                sKey = item.down('.key').innerHTML;
            } catch (e) {
                alert('Warning: Carousel Items require a child with classname [key]');
                return;
            }

            var oCarouselItem = new CarouselItem();
            if (this.options.itemParser)
				oCarouselItem.value = this.options.itemParser(item);
            oCarouselItem.index = this.items.length;
            oCarouselItem.key = sKey;
            oCarouselItem.element = item;
            this.items.push(oCarouselItem);

            var iNewLeft = - ((oCarouselItem.index-2) * (this.itemWidth + (15 + 2)));

            this.leftStyleData[oCarouselItem.index] = iNewLeft;

            //Store default selection
            if (item.hasClassName('itemSelected')) {
                this.activeItem = oCarouselItem;
                this.activeIndex = this.items.length - 1;
            }

            if (this.options.setItemEvents) {
                this.options.setItemEvents(this, item, oCarouselItem, this.observer);
            }
        } .bind(this));

        //Post processing
        this.loaded = true;
        this.afterLoad();
    },
    destroy: function() {
        this.loaded = false;
        var eList = this.itemsElement;
        this.items.clear();
        if (this.options.unsetItemEvents) {
            eList.select('.item').each(function(item, ix) {
                this.options.unsetItemEvents(this, item, this.items[ix], this.observer);
            } .bind(this));
        }
    },
    afterLoad: function() {
        if (this.items.length == 0) {
            return;
        }

        //Change the following line to moveToIndex if you do
        //not want the load animation on default selected items
        this.moveToIndex(this.activeIndex);

        if (this.observer.fireActiveCarouselLoaded) {
            this.observer.fireActiveCarouselLoaded(this);
        }
    },
    scrollForward: function() {
        //setsize-1 at a time scrolling
        var iIndex = 0;

        if (this.navScrollIndex > this.items.length - (this.options.setSize)) {
            if (!this.options.allowAutoLoopOnSet) {

                return;
            }
        } else {
            iIndex = this.navScrollIndex + (this.options.setSize - 1);
        }
        this.scrollToIndex(iIndex);
    },
    scrollBack: function() {
        var iIndex = this.navScrollIndex - (this.options.setSize - 1);
        if (iIndex < 0) {
            if (!this.options.allowAutoLoopOnSet) {
                iIndex = 0;
            } else {
                iIndex = this.items.length - this.options.setSize - 1;
                if (this.navScrollIndex > 0 || iIndex < 0) iIndex = 0;
            }
        }

        this.scrollToIndex(iIndex);
    },
    scrollForwardAndStop: function() {
        // need to clear event observing since
        if (this.forwardElement) Event.stopObserving(this.forwardElement, 'click');

        // clearInterval when button is pressed, since it would scroll
        // twice if this is not done
        clearInterval(Mxm.Slider.Auto.slideLeft);

        this.next();

        // reset interval
        Mxm.Slider.Auto.slideLeft = setInterval(Mxm.Slider.Auto.slideNext, 5000);
        if (this.forwardElement) Event.observe(this.forwardElement, 'click', this.scrollForwardAndStop.bind(this));
    },
    scrollBackAndStop: function() {
        if (this.backElement) Event.stopObserving(this.backElement, 'click');
        clearInterval(Mxm.Slider.Auto.slideLeft);

        this.previous();

        Mxm.Slider.Auto.slideLeft = setInterval(Mxm.Slider.Auto.slideNext, 5000);
        if (this.backElement) Event.observe(this.backElement, 'click', this.scrollBackAndStop.bind(this));
    },
    getLeft: function(index) {
        return index * (-this.itemWidth);
    },
    getTop: function(index) {
        return index * (-this.itemHeight);
    },
    activate: function(carouselItem) {
        if (this.activeItem) {
            this.observer.fireDeactiveCarouselItem(this, this.activeItem.element, this.activeItem);
        }

        if (! carouselItem) {
			var carouselItem = this.items[this.options.setSize];
        }

	    this.activeItem = carouselItem;

        if (this.countDone == 0) {
            this.pushElms(this.activeIndex);
        }

        ++this.countDone;

        if (this.observer.fireActiveCarouselItem){
            this.observer.fireActiveCarouselItem(this, carouselItem.element, carouselItem);
        }
    },
    reactivate: function() {
        if (!this.activeItem) return;
        this.activate(this.activeItem);
    },
    pushElms : function(until) {
        for (var i = 0; i < until; i++) {
            this.items.push( Mxm.Editos.createItemDOM(
                $('edito').down('.items'),
                (this.items.length),
                this.items[i].element.down('.picture').childNodes[0].nodeValue,
                this.items[i].element.down('.caption').childNodes[0].nodeValue,
                this.items[i].element.down('.caption').childNodes[0].nodeValue,
                this.items[i].element.down('.icon').down('img').getAttribute('src'),
                true, false
            ) );
        }
    },
    next: function() {
        if (this.activeItem == null) {
            this.activate(this.items[this.options.setSize]);
            this.activeIndex = this.options.setSize;
            return;
        }

        this.currentLeft = parseInt(this.itemsElement.getStyle('left'));

        if (typeof(this.oldLeft) != 'undefined' && this.currentLeft == this.oldLeft) {
            // has not scrolled for previous element (speed click)
            var move = (this.itemWidth + (parseInt(this.activeItem.element.getStyle('margin-right')) || 0) + 2);
            this.currentLeft = this.currentLeft - move;
        }

        this.oldLeft = this.currentLeft;

        // ALLOW LOOP RIGHT
        var widthAccordingToCountItems = (this.items.length+1) * 150;

        $('edito').down('.items').setStyle({width: widthAccordingToCountItems + 'px'});

        this.items.push( Mxm.Editos.createItemDOM(
            $('edito').down('.items'),
            (this.items.length),
            this.activeItem.element.down('.picture').childNodes[0].nodeValue,
            this.activeItem.element.down('.caption').childNodes[0].nodeValue,
            this.activeItem.element.down('.caption').childNodes[0].nodeValue,
            this.activeItem.element.down('.icon').down('img').getAttribute('src'),
            true, false
        ) );
        // END OF LOOP

        var iIndex = this.activeItem.index + 1;
        if (this.countDone == 0) {
            // first time
            iIndex = iIndex + 1;
        }

        if (iIndex > this.items.length) {
            iIndex = 0;
            if (!this.options.allowAutoLoopOnIndividual) iIndex = this.items.length - 1;
        }

        this.oldIndex = this.activeIndex;
        this.activeIndex = iIndex;

		if ((iIndex) - (this.options.setSize-1) >= this.navScrollIndex - 1) {
			this.scrollForward();
        }
        else {
            this.activate(this.items[iIndex]);
        }
    },
    previous : function() {
       if (this.activeItem == null) { this.activate(this.items[0]); return; }
        var iIndex = this.activeIndex - 1;
        if (iIndex < 0) {
            if (this.options.allowAutoLoopOnIndividual) {
                iIndex = this.items.length - 1;
            } else {
                iIndex = 0;
            }
        }

        if (iIndex == 0) { this.scrollToIndex(0); return; }
        if (iIndex == this.items.length - 1) {
            var iNavIndex = this.items.length - this.options.setSize - 1;
            if (iNavIndex < 0) iNavIndex = 0;
            this.scrollToIndex(iNavIndex); return;
        }

        this.activate(this.items[iIndex]);
        this.oldIndex = this.activeIndex;
        this.activeIndex = iIndex;

        if (iIndex < this.navScrollIndex + 1) this.scrollBack();
    },
    scrollToIndex: function(index, duration) {
        if (index < 0) index = this.activeIndex;
        duration = duration || this.options.duration; //allow for override
        if (this.options.direction == 'vertical') {
            var iPreviousTop = this.getTop(this.navScrollIndex);
            var iTop = this.getTop(index);
            var iCurrentTop = parseInt(Element.getStyle(this.itemsElement, 'top')) || 0;
            var offset = iPreviousTop - iCurrentTop;
            var move = iTop - iPreviousTop;
            if (move > 0) {
                move = move + offset;
            } else {
                move = move - offset;
            }
            Element.setOpacity(this.itemsElement, this.options.moveOpacity);
            var ef = new Effect.Move(this.itemsElement, {
                'duration': duration,
                'y': move,
                'afterFinish': function() {
                    Element.setStyle(this.itemsElement, { 'top': iTop + 'px' });
                    Element.setOpacity(this.itemsElement, 1.0);
                } .bind(this)
            });
            ef = null;
        } else {

            // need to move the width of the item + padding
            // +2 at the end because of border !!
            var mode = 0;
            if (! this.oldIndex) {
                this.oldIndex = index;
            }

            if (index < (this.oldIndex)) {
                // scroll right
                move = (this.itemWidth + (parseInt(this.activeItem.element.getStyle('margin-right')) || 0) + 2);
            } else {
                move = - (this.itemWidth + (parseInt(this.activeItem.element.getStyle('margin-right')) || 0) + 2);
            }

            var iCurrentLeft = this.currentLeft;
            var iNewLeft = 0;
            // move will always be negative
            if (index < (this.oldIndex)) {
                if (this.leftStyleData[index] != 'undefined') {
                    // use saved data, don't calculate
                    iNewLeft = this.leftStyleData[index];
                }
                else {
                    if (index > 1) {
                        iNewLeft = ((index-2) * (this.itemWidth + (parseInt(this.activeItem.element.getStyle('margin-right'))) + 2));
                    }
                    else {
                        iNewLeft = ((-(index-2)) * (this.itemWidth + (parseInt(this.activeItem.element.getStyle('margin-right'))) + 2));
                    }
                }
            }
            else {
                iNewLeft = - ((index-2) * (this.itemWidth + (parseInt(this.activeItem.element.getStyle('margin-right'))) + 2));
            }

            if (this.leftStyleData[index] == 'undefined') {
                this.leftStyleData[index] = iNewLeft;
            }

            Element.setOpacity(this.itemsElement, this.options.moveOpacity);
            var ef = new Effect.Move(this.itemsElement, {
                'duration': duration,
                'x': move,
                'afterFinish': function() {
                    Element.setStyle(this.itemsElement, { 'left': iNewLeft + 'px' });
                    Element.setOpacity(this.itemsElement, 1.0);
                } .bind(this)
            });
            ef = null;
        }
        this.navScrollIndex = index;

		if (this.forwardElement && this.backElement) {
	        Element.display(this.forwardElement, this.navScrollIndex <= this.items.length - (this.options.setSize + 1) || this.options.allowAutoLoopOnSet);
	        Element.display(this.backElement, (parseInt(this.navScrollIndex) || 0) != 0 || this.options.allowAutoLoopOnSet);
		}
        if (this.observer.fireCarouselAtIndex) this.observer.fireCarouselAtIndex(this, index);

        this.activate(this.items[index]);
    },
    moveToIndex: function(index) {
        if (this.options.direction == 'vertical') {
            var iTop = this.getTop(index);
            Element.setStyle(this.itemsElement, { 'top': iTop + 'px' });
            Element.setOpacity(this.itemsElement, 1.0);
        } else {
            var iNewLeft = this.leftStyleData[index-1];
            var iWidth = (this.items.length+1) * 150;
            Element.setStyle(this.itemsElement, {
                'left': iNewLeft + 'px',
                'width' : iWidth + 'px'
            });
            Element.setOpacity(this.itemsElement, 1.0);
        }
        this.navScrollIndex = index;

		if (this.forwardElement && this.backElement) {
	        Element.display(this.forwardElement, this.navScrollIndex <= this.items.length - (this.options.setSize + 1) || this.options.allowAutoLoopOnSet);
	        Element.display(this.backElement, (parseInt(this.navScrollIndex) || 0) != 0 || this.options.allowAutoLoopOnSet);
		}

        this.activate(this.items[index]);
    }
};



