Carousel = new Class({
    Implements: [Options],
    options: {
		/*
         container: null,
         */
		opacity: .1,
		scrollSetLength: 1,
		callback: $empty
    },
    initialize: function(options){
        this.setOptions(options);
        this.bootstrapElements();
        this.updateDisplay();
    },
    updateDisplay: function(){
        if (this.getAllElements().length != 0) {
            this.determineActiveElement();
            this.updateElementStyles();
            this.centerActiveElement();
        }
    },
    determineActiveElement: function(){
        if (!this.activeElement) {
			var allElements = this.getAllElements();
            var elements = this.getAllElements('.active');
            if (elements[0]) {
				this.setActiveElement(elements.shift());
			}
            else if (allElements.length > 1) {
				this.setActiveElement(allElements[Math.ceil(allElements.length / 2)]);
			}
        }
    },
    setActiveElement: function(element){
		if (!element) { return; }
		
        this.activeElement = element;
        this.updateDisplay();
		clearTimeout(this.setActiveTimeout);

		var _this = this;
		var callback = function() {
			_this.options.callback(element.id.split('_').pop());
		}
        this.setActiveTimeout = setTimeout(callback, 500);
    },
    updateElementStyles: function(){
		var _this = this;
        var length = this.getNonActiveElements().length;
        for (var i = 0; i < length; i++) {
            var item = this.getNonActiveElements()[i];
            if (item.hasClass('active')) {
                if (this.makeActiveEffect) 
                    this.makeActiveEffect.cancel();
                item.removeClass('active');
                this.makeInactiveEffect = new Fx.Morph(item, {
                    duration: 'short',
                    transition: Fx.Transitions.Sine.easeOut
                }).start({
                    'opacity': _this.options.opacity
                });
            }
        }
        
        this.activeElement.addClass('active');
        this.makeActiveEffect = new Fx.Morph(this.activeElement, {
            duration: 'long',
            transition: Fx.Transitions.Sine.easeOut
        }).start({
            'opacity': 1
        });
    },
    centerActiveElement: function(){
        var parent = this.options.container;

        new Fx.Morph(parent, {
            duration: 700,
            transition: Fx.Transitions.Quart.easeOut
        }).start({
            'left': Math.ceil(Math.ceil(this.options.container.getParent().getSize().x / 2) - (this.activeElement.getPosition(parent).x + Math.ceil(this.activeElement.getSize().x / 2)))
        });
    },
    bootstrapElements: function(){
        var _this = this;

        this.getAllElements().addEvent('click', function(event){
            _this.handleElementClick(event);
        });
        
        var containerParent = this.options.container.getParent();
        var viewPort = new Element('div', {
            styles: {
                overflow: 'hidden',
                position: 'relative',
                height: containerParent.getSize().y
            }
        });
        var leftArrow = new Element('div', {
            styles: {
                position: 'absolute'
            },
            'class': 'carousel-left-arrow'
        });
        var rightArrow = new Element('div', {
            styles: {
                position: 'absolute'
            },
            'class': 'carousel-right-arrow'
        });
        
        containerParent.adopt(viewPort);
        
        viewPort.adopt(this.options.container);
		viewPort.setStyles({
			width: (containerParent.getSize().x - 100),
			left: 50
		});
		containerParent.adopt([leftArrow, rightArrow]);
		containerParent.setStyle('position', 'relative')
        this.options.container.setStyle('top', 10);
        
        var arrowTop = function(arrow, container){
            return (container.getSize().y - arrow.getSize().y - 24) / 2
        }
        
        leftArrow.setStyles({
            'top': arrowTop(leftArrow, viewPort)
        }).addEvent('click', function(event) {
            event.stop();
            _this.handleLeftArrowClick();
        }).addEvent('doubleclick', function(event) {
			event.stop();
		});
		
        rightArrow.setStyles({
            'top': arrowTop(rightArrow, viewPort)
        }).addEvent('click', function(event) {
            event.stop();
            _this.handleRightArrowClick();
        });
        
        this.options.container.setStyle('position', 'absolute');
        this.getAllElements().setStyles({
            position: 'absolute',
            opacity: this.options.opacity
        });
        
        this.distributeChildren();
    },
    distributeChildren: function(){
        // Space out children evenly
        var total = 0;
        var minGutter = 20;
        
        this.getAllElements().each(function(item, index){
            total += item.getSize().x;
        });
        
        var length = this.getAllElements().length
        
        // Resize parent to include children and minimum delta
        if (this.options.container.getSize().x < total) 
            this.options.container.setStyle('width', total + length * minGutter)
        
        var delta = this.options.container.getSize().x - total;
        
        var perItemMargin = delta / length;
        
        for (var i = 0; i < length; i++) {
            var previousElement = this.getAllElements()[i - 1];
            var previousItemRightEdge = previousElement ? previousElement.getPosition(previousElement.getParent()).x + previousElement.getSize().x : 0;
            this.getAllElements()[i].setStyle('left', previousItemRightEdge + perItemMargin);
        }
    },
    getAllElements: function(selector){
        return this.options.container.getChildren(selector);
    },
    getNonActiveElements: function(){
        var activeElement = this.activeElement;
        return this.getAllElements().filter(function(item){
            return item != activeElement;
        });
    },
    handleElementClick: function(event){
		if (!event.getTarget()) { return; }
        if (event.getTarget().getParent() === this.options.container) {
            var element = event.getTarget();
        }
        else {
            var trueTarget;
            var node = event.getTarget();
            while (node && !trueTarget) {
                if (node.getParent() === this.options.container) 
                    trueTarget = node;
                else 
                    node = node.getParent();
            }
            element = trueTarget;
        }
        this.setActiveElement(element);
    },
    handleLeftArrowClick: function(){
        if (this.activeElement !== this.getAllElements()[1]) {
			var allPrevious = this.activeElement.getAllPrevious();
			var element = allPrevious[this.options.scrollSetLength - 1] || allPrevious[allPrevious.length - 1];
            this.setActiveElement(element);
		}
    },
    handleRightArrowClick: function(){
        if (this.activeElement !== this.getAllElements()[this.getAllElements().length - 2]) {
			var allNext = this.activeElement.getAllNext();
			var element = allNext[this.options.scrollSetLength - 1] || allNext[allNext.length - 1];
            this.setActiveElement(element);
		}
    }
})
