﻿(function ($, undefined) {

    var $window = $(window);

    var defaults = {
        btnPrev: null,
        btnNext: null,
        btnGo: null,
        auto: null,

        speed: 'fast',
        easing: null,

        editMode: false,
        vertical: false,
        circular: true,
        visible: 3,
        start: 0,
        scroll: 1,

        hideScrollButtons: false,
        resizeListItems: false,
        beforeStart: null,
        afterEnd: null,
        afterScroll: null
    };

    function Layout(dimension, position, float) {
        this.d = dimension;
        this.p = position;
        this.itemFloat = float;
    }

    Layout.prototype = {
        dimension: function (element, value) {
            return element[this.d](value);
        },
        positionCss: function (value) {
            var result = {};
            result[this.p] = value;
            return result;
        },
        setPosition: function (element, value) {
            element.css(this.p, value);
        }
    };

    var VerticalLayout = new Layout('height', 'top', 'none');
    var HorizontalLayout = new Layout('width', 'left', 'left');
    var HorizontalRtlLayout = new Layout('width', 'right', 'right');

    function Filmstrip(e, options) {
        
        var self = this;
        var rtl  = false;
        var autoEnabled, autoTimer;
        var first, last, layout, animating;
        var $container, $list, $items;

        var $btnNext, $btnPrev;

        $container = $(e);
        $list      = $container.find('ul, ol').first();
        $items     = listItems();

        options    = $.extend({}, defaults, options);
        rtl        = ($container.css('direction') == 'rtl')
        first      = options.start;
        last       = first + options.visible;
        layout     = options.vertical ? VerticalLayout : rtl ? HorizontalRtlLayout : HorizontalLayout;
        animating  = false;
        
        autoTimer   = null;
        autoEnabled = false;

        self.$container = $container;
        self.$list = $list;

        if (options.btnNext) {
            $btnNext = $(options.btnNext);
            $btnNext.click(function (e) {
                self.next();
                e.preventDefault();
            });
            self.$btnNext = $btnNext;
        }

        if (options.btnPrev) {
            $btnPrev = $(options.btnPrev);
            $btnPrev.click(function (e) {
                self.prev();
                e.preventDefault();
            });
            self.$btnPrev = $btnPrev;
        }

        function listItems() {
            return $list.children('li');
        }

        function updateItems() {
            $items = listItems();

            if (options.resizeListItems) {
                var itemDimension = Math.floor(layout.dimension($container) / options.visible);
                layout.dimension($items, itemDimension);
            }

            $items.css({
                height: '100%', 
                display: 'block',
                float:   layout.itemFloat
            });
        }

        function updateList() {
            updateItems();
            
            var width = 0;
            $items.each(function () { width += $(this).outerWidth(true); });
            
            layout.dimension($list, width);
            $list.css('height', '100%');
        }

        function pos(delta, animate, userCallback) {
            if (animating) {
                return;
            }

            var newFirst = first + (delta || 0);
            var newLast  = newFirst + options.visible;
            var $itemsToClone, $clonedItems;
            
            updateList();
            pauseAutoScroll();

            if (options.visible >= $items.length) {
                newFirst = 0;
            }
            else if (newFirst < 0) {
                $itemsToClone = $items.slice(newFirst); 
                $clonedItems = 
                    $itemsToClone
                    .clone()
                    .prependTo($list);
                updateList();
                newFirst += $itemsToClone.length;

                if ($clonedItems) {
                    var pos = 0;
                    $clonedItems.each(function () { pos -= $(this).outerWidth(true); });
                    layout.setPosition($list, pos);
                }
            } 
            else if (newLast > $items.length) {
                $itemsToClone = $items.slice(0, newLast - $items.length);
                $clonedItems =
                    $itemsToClone
                    .clone()
                    .appendTo($list);

                updateList();
            } 

            var destPos = 0;
            $items.slice(0, newFirst).each(function () { destPos -= $(this).outerWidth(true); });
            var properties = layout.positionCss(destPos);

            if (animate) {
                animating = true;
                $list.animate(properties, options.speed, function () {
                    animating = false;
                    callback();
                });
            }
            else {
                $list.css(properties);
                callback();
            }

            function callback() {
                if ($itemsToClone) {
                    $itemsToClone.remove();
                    updateList();

                    if (newLast > $items.length) {
                        newFirst -= $itemsToClone.length;

                        var destPos = 0;
                        var includeMargin = true;
                        $items.slice(0, newFirst).each(function () { destPos -= $(this).outerWidth(includeMargin); });
                        layout.setPosition($list, destPos);
                    }
                }

                first = newFirst;
                last  = newLast;
                self.first = newFirst;

                if (options.afterScroll) {
                    options.afterScroll(self);
                }

                if (userCallback) {
                    userCallback(self);
                }

                if (autoEnabled) {
                    autoScroll();
                }
            }
        }

        function pauseAutoScroll() {
            if (autoTimer !== null) {
                window.clearTimeout(autoTimer);
                autoTimer = null;
            }
        }

        function autoScroll(timeout) {
            if (timeout !== undefined) {
                options.auto = timeout;
            }

            pauseAutoScroll();
            autoEnabled = (0 < options.auto);

            if (autoEnabled) {
                autoTimer = window.setTimeout(function () { 
                    self.next(); 
                }, options.auto);
            }
        }

        function ensureVisible(index, callback) {
            if (index < first) {
                pos(index - first, true, callback);
            }
            else if (index >= first + options.visible) {
                pos(index - (first + options.visible) + 1, true, callback);
            }
            else {
                callback(self);
            }
        }

        function update() {
            delete self.next;
            delete self.prev;

            pos(0);

            if ((options.visible < $items.length) && (options.scroll < $items.length)) {
                $btnNext.show();
                $btnPrev.show();

                var scroll = options.scroll;

                if (rtl) {
                    scroll = -scroll;
                }

                self.next = function () {
                    pos(+scroll, true);
                };
                self.prev = function () {
                    pos(-scroll, true);
                };
            }
            else if (options.hideScrollButtons) {
                $btnNext.css('visibility', 'hidden');
                $btnPrev.css('visibility', 'hidden');
            }
        }

        $container.css({
            overflow: 'hidden',
            position: 'relative'
        });

        $window.bind('resize.filmstrip', function () {
            pos(0);
        });

        $list.css({
            margin:   0,
            padding:  0,
            border:   'none',
            display:  'block',
            position: 'absolute',
            listStyleType: 'none'
        });

        self.update = update;
        self.autoScroll = autoScroll;
        self.pauseAutoScroll = pauseAutoScroll;
        self.resumeAutoScroll = function () { autoScroll(); }
        self.ensureVisible = ensureVisible;

        self.options = options;

        setTimeout(function () {
            self.update();
            autoScroll();
        }, 0);
    }

    Filmstrip.fn = Filmstrip.prototype = {
        filmstrip: '1.0.0',
        next: $.noop,
        prev: $.noop
    };

    $.fn.filmstrip = function () {
        var fs;
        var slice = Array.prototype.slice;

        if (typeof arguments[0] === 'string') {
            fs = this.data('filmstrip');
            fs[arguments[0]].apply(fs, slice.call(arguments, 1));
        }
        else {
            fs = new Filmstrip(this, arguments[0]);
            this.data('filmstrip', fs);
        }

        return this;
    }

})(jQuery);
