var FadingSlideshow = new Class({
    Implements: [Options, Events],

    options: {
        intervalTimer: 10000,
        slideSelector: '.slide',
        arrowLeftCaption: '&lArr;',
        arrowRightCaption: '&rArr;',
        tweenOptions: {
            duration: 1200
        }
    },

    initialize: function(viewport, options)
    {
        var v = $(viewport), o = v.retrieve('FadingSlideshow', false);
        if (o) return o;
        this.setOptions(options);
        this.attach(v);
    },

    attach: function(viewport)
    {
        // *** Viewport
        this.viewport = viewport;
        viewport.store('FadingSlideshow', this);
        // *** Arrows
        var self = this;
        this.arrowLeft = new Element('div.arrow.left', {
            html: '<span>' + this.options.arrowLeftCaption + '</span>'
        }).inject(this.viewport, 'after').addEvent('click', function(e) {
            e.stop();
            self.previous();
        });
        this.arrowRight = new Element('div.arrow.right', {
            html: '<span>' + this.options.arrowRightCaption + '</span>'
        }).inject(this.arrowLeft, 'after').addEvent('click', function(e) {
            e.stop();
            self.next();
        });
        // *** Points
        this.indicator = new Element('div.indicator').inject(this.arrowRight, 'after');
        // *** Slides
        var slides = this.viewport.getElements(this.options.slideSelector);
        if (slides.length == 0) throw 'No slides in the slider';
        this.slides = slides;
        this.active = this.slides[0];
        if (this.slides.length < 2) return this;
        var points = [];
        this.slides.each(function(slide) {
            var point = new Element('div.point').adopt(
                new Element('span').set('tween', self.options.tweenOptions)
            ).addEvent('click', function(e) {
                e.stop();
                self.show(slide);
            }).inject(self.indicator, 'bottom');
            slide.store('FadingSlideshowPoint', point);
            if (slide == self.active) {
                slide.setStyles({
                    opacity: 1,
                    display: ''
                }).addClass('active');
                point.addClass('active');
            } else {
                slide.setStyles({
                    opacity: 0,
                    display: 'none'
                });
            }
            points.push(point);
            var fst = slide.retrieve('FadingSlideshowTween', false);
            if (!fst) slide.store('FadingSlideshowTween', new Fx.Tween(slide, self.options.tweenOptions).addEvent('complete', function() {
                if (self.active != slide) slide.setStyle('display', 'none');
                self.animating = false;
            }));
            else fst.setOptions(self.options.tweenOptions);
        });
        this.points = points;
        // *** Center indicator
        var vpWidth = this.indicator.getParent().getStyle('width').toInt();
        var inWidth = this.indicator.getSize().x;
        this.indicator.setStyle('left', Math.round((vpWidth - inWidth) / 2));
        // *** Timeout
        this.timeout = window.setTimeout(function() {
            self.timeout = null;
            self.next();
        }, this.options.intervalTimer);
        this.fireEvent('attach', viewport);
        return this;
    },

    detach: function()
    {
        // *** Timeout
        if (this.timeout) window.clearTimeout(this.timeout);
        delete this.timeout;
        // *** Slides
        this.slides.each(function(slide) {
            slide.setStyles({
                opacity: '',
                display: ''
            });
            slide.eliminate('FadingSlideshowPoint');
        });
        this.active.removeClass('active');
        this.points.each(function(point) {
            point.destroy();
        });
        delete this.active;
        delete this.slides;
        delete this.points;
        // *** Indicator
        this.indicator.destroy();
        delete this.indicator;
        // *** Arrows
        this.arrowLeft.destroy();
        this.arrowRight.destroy();
        delete this.arrowLeft;
        delete this.arrowRight;
        // *** Viewport
        this.viewport.eliminate('FadingSlideshow');
        var viewport = this.viewport;
        delete this.viewport;
        this.fireEvent('detach', viewport);
        return this;
    },

    next: function()
    {
        if (this.animating) return this;
        this.animating = true;
        var i = this.slides.indexOf(this.active),
            k = i + 1;
        if (!this.slides[k]) k = 0;
        var next = this.slides[k];
        if (this.timeout) window.clearTimeout(this.timeout);
        var self = this;
        this.timeout = window.setTimeout(function() {
            self.timeout = null;
            self.next();
        }, this.options.intervalTimer);
        this.fireEvent('next', next);
        this.show(next);
        return this;
    },

    previous: function()
    {
        if (this.animating) return this;
        this.animating = true;
        var i = this.slides.indexOf(this.active),
            k = i - 1;
        if (k < 0) k = this.slides.length - 1;
        var next = this.slides[k];
        this.fireEvent('previous', next);
        this.show(next);
        return this;
    },

    show: function(slide)
    {
        if (slide == this.active) return this;
        slide.setStyles({
            opacity: 0,
            display: ''
        });
        var a = this.active.removeClass('active');
        this.active = slide.addClass('active');
        var to = a.retrieve('FadingSlideshowTween'),
            ti = slide.retrieve('FadingSlideshowTween'),
            po = a.retrieve('FadingSlideshowPoint'),
            pi = slide.retrieve('FadingSlideshowPoint');
        po.removeClass('active').getElement('> span').fade('out');
        pi.addClass('active').getElement('> span').setStyle('opacity', 0).fade('in');
        to.start('opacity', 0);
        ti.start('opacity', 1);
        this.fireEvent('show', slide);
        return this;
    },

    toElement: function()
    {
        return this.viewport;
    }
});
