/*
Slideshow

Inspiration for non-JS version: http://sixrevisions.com/tutorials/javascript_tutorial/create-a-slick-and-accessible-slideshow-using-jquery/
*/
(function ($) {
    $.fn.slideshow = function (options) {
        var settings = {
                timeout: 3000, // time for which slides get displayed (minus transition duration)
                duration: 'slow' // transition duration; accepts any speed jQuery.animate() does
            };
        
        return this.each(function () {
            var $this = $(this), $slides, currentIndex = 0, $display,
                $displayWrapper, $slidesContainer, slideDimensions,
                controls = [], autoplayTimer;
            
            function updateControls() {
                var i;
                
                for (i = 0; i < controls.length; i += 1) {
                    if (typeof controls[i].update === 'function') {
                        controls[i].update();
                    }
                }
            }
            
            function showSlide(slideIndex) {
                var direction = 'rtl';
                
                function slideCallback() {
                    var oldSlideIndex = (direction === 'ltr' ? 1 : 0);
                    
                    // move former slide back to $slidesContainer
                    $displayWrapper.children().eq(oldSlideIndex) // should be old slide
                        .appendTo($slidesContainer);
                    // update controls (prev/next, direct selection)
                    updateControls();
                    // notify new slide
                    $displayWrapper.children().eq(0) // should now be the new slide
                        .trigger('init.slideshow');
                }
                
                if (slideIndex === currentIndex) {
                    return;
                }
                
                // find out direction (default: right-to-left)
                if ((slideIndex < currentIndex // e.g. 3 to 1
                        && !(currentIndex === $slides.length - 1 && slideIndex === 0)) // not: end to start (carousel)
                    || (slideIndex === $slides.length - 1 // but also start to end
                        && currentIndex === 0)) {
                    direction = 'ltr';
                }
                
                if (direction === 'ltr') {
                    $slides.eq(slideIndex).css('margin-left', -slideDimensions[0])
                        .prependTo($displayWrapper);
                    $displayWrapper.children().eq(1).trigger('destroy.slideshow')
                        .prev().animate({marginLeft: 0}, settings.duration, slideCallback);
                } else {
                    // position new slide next to current slide
                    //   read: move element form $slidesContainer to $display
                    $slides.eq(slideIndex).appendTo($displayWrapper).css('margin', 0);
                    // transition to new slide (notify current slide)
                    //   ready callback: slideCallback()
                    $displayWrapper.children().eq(0).trigger('destroy.slideshow')
                        //.end().end().animate({left: -slideDimensions[0]}, 'slow', slideCallback);
                        .animate({marginLeft: -slideDimensions[0]}, settings.duration, slideCallback);
                }
                currentIndex = slideIndex;
            }
            
            function setupAutoplay() {
                function safeStartTimer() {
                    if (autoplayTimer) {
                        window.clearTimeout(autoplayTimer);
                    }
                    autoplayTimer = window.setTimeout(timerHandler, settings.timeout);
                }

                function timerHandler() {
                    var newIndex = currentIndex + 1;
                    if (newIndex > $slides.length - 1) {
                        newIndex = 0;
                    }
                    showSlide(newIndex);
                    // Schedule next call
                    safeStartTimer();
                }
                
                // set timer to call showSlide(currentindex + 1)
                autoplayTimer = window.setTimeout(timerHandler, settings.timeout);
                // prevent timer from firing while the user hovers the slides
                $this.mouseenter(function () {
                        window.clearTimeout(autoplayTimer);
                    }).mouseleave(function () {
                        safeStartTimer();
                    });
            }
            
            function getControlsWrapper() {
                var $controls = $this.find('.slideshow-controls');
                
                if ($controls.length === 0) {
                    $controls = $('<div class="slideshow-controls" />')
                        .insertAfter($display);
                }
                return $controls;
            }
            
            function setupDirectSelection() {
                var $controls, i;
                
                function clickHandler(e) {
                    var data;
                    e.preventDefault();
                    data = $(this).data('slideshow');
                    showSlide(data.index);
                }
                
                // generate HTML for slide navigation
                $controls = $('<span class="direct-selection" />').appendTo(getControlsWrapper());
                for (i = 0; i < $slides.length; i += 1) {
                    $('<span>' + String(i + 1) + '</span>')
                        .data('slideshow', {index: i})
                        .appendTo($controls).click(clickHandler);
                }
                // wire buttons to call showSlide()
                controls.push({
                    update: function () {
                        $controls.find('span').filter('.active').removeClass('active')
                            .end().eq(currentIndex).addClass('active');
                    }
                });
            }
            
            function setupPrevNext() {
                var $controls;
                // generate HTML for prev/next buttons
                // wire buttons to call showSlide(currentindex +/- 1)
                $controls = getControlsWrapper();
                $('<span class="prev">&lt;</span>').prependTo($controls).click(function () {
                    var newIndex;
                    newIndex = currentIndex - 1;
                    if (newIndex < 0) {
                        newIndex = $slides.length - 1;
                    }
                    showSlide(newIndex);
                });
                $('<span class="next">&gt;</span>').appendTo($controls).click(function () {
                    var newIndex;
                    newIndex = currentIndex + 1;
                    if (newIndex > $slides.length - 1) {
                        newIndex = 0;
                    }
                    showSlide(newIndex);
                });
            }
            
            function init() {
                $slidesContainer = $this.find('.slides-container');
                $slides = $slidesContainer.find('.slide');
                
                // thrash no-js-solution
                slideDimensions = [$slidesContainer.width(), $slidesContainer.height()];
                $slidesContainer.hide();
                $display = $('<div />').css({
                        width: slideDimensions[0],
                        height: slideDimensions[1],
                        overflow: 'hidden',
                        position: 'relative'
                    })
                    .insertBefore($slidesContainer);
                $displayWrapper = $('<div />').css('width', slideDimensions[0] * $slides.length).appendTo($display);
                $slides.css({float: 'left', width: slideDimensions[0]});
                $slides.eq(currentIndex).appendTo($displayWrapper);
                
                // setup controls as needed
                setupDirectSelection();
                setupPrevNext();
                setupAutoplay();
                
                // notify first slide like slideCallback would
                $slides.eq(currentIndex).trigger('init.slideshow');
                updateControls();
            }
            
            if (options) {
                $.extend(settings, options);
            }
            init();
        });
    };
}(jQuery));
