window.cmu = window.cmu || {};

(function (ns) {

    ns.filmStrip = function (spec, my) {
        var that = {};

        spec = spec || {};
        spec.minimumItems = spec.minimumItems || 4;
        spec.viewPort = spec.viewPort || '.viewPort';
        spec.strip = spec.strip || 'ul.images';
        spec.firstItem = spec.firstItem || 0;
        spec.widthOffset = spec.widthOffset !== undefined ? spec.widthOffset : 10;
        spec.navigationBatchSize = spec.navigationBatchSize || 3;
        spec.autoNavEnabled = spec.autoNavEnabled !== undefined ? spec.autoNavEnabled : true;
        spec.autoNavSpeed = spec.autoNavSpeed || 650;

        // ------------------------------------------------------------------
        // Shared properties
        // ------------------------------------------------------------------

        my = my || {};
        my.logger = gj.logging.getLogger('cmu.filmStrip');

        my.$viewNode = spec.viewNode;

        my.buttons = {
            previous: my.$viewNode.find('ul.navigation li.prev a'),
            next: my.$viewNode.find('ul.navigation li.next a')
        };
        my.navigationInterval = null;

        // ------------------------------------------------------------------
        // Private properties
        // ------------------------------------------------------------------

        my.$viewPort = my.$viewNode.find(spec.viewPort);
        my.$strip = my.$viewNode.find(spec.strip);
        my.$stripItems = my.$strip.children();

        // ------------------------------------------------------------------
        // Private methods
        // ------------------------------------------------------------------

        var init = function () {
            my.logger.debug('init(), viewNode:', my.$viewNode);

            if (my.$stripItems.length < spec.minimumItems) {
                my.logger.info('Not enough items to enable film strip');
                return null;
            }
            my.$viewNode.addClass('jsEnabled');

            // Critical: calculate dimensions after jsEnabled class has been applied
            my.itemWidth = my.$stripItems.eq(0).outerWidth(true);
            my.stripWidth = my.$stripItems.length * my.itemWidth;
            my.viewPortWidth = my.$viewPort.outerWidth(false);
            my.maxItem = calculateMaxItem();

            // Set the correct strip width to match the number of strip items
            my.$strip.css({'width': my.stripWidth});

            window.setTimeout(function () {
                draw();
            }, 0);

            initButtons();

            return that;
        };

        var calculateMaxItem = function () {
            my.logger.debug('calculateMaxItem()');
            var visibleItemCount = Math.floor(my.viewPortWidth / my.itemWidth);
            return my.$stripItems.length - visibleItemCount;
        };

        var initButtons = function () {
            my.logger.debug('initButtons()');
            var buttonName, button;

            if (spec.autoNavEnabled) {
                for (buttonName in my.buttons) {
                    button = my.buttons[buttonName];

                    // START auto-navigating on mousedown
                    button.mousedown((function (func) {
                        return function () {
                            autoNavStart(func);
                        };
                    })(that[buttonName]));

                    // STOP auto-navigating on mouseup
                    button.mouseup(autoNavStop);

                    // Prevent normal anchor behavior
                    button.click(function () {
                        return false;
                    });
                }
            } else {
                for (buttonName in my.buttons) {
                    my.buttons[buttonName].click((function (func) {
                        return func;
                    })(that[buttonName]));
                }
            }
        };

        var draw = function () {
            my.logger.debug('draw()');

            var leftPos = my.itemWidth * spec.firstItem * -1;

            var leftPosMax = (my.stripWidth - my.viewPortWidth + spec.widthOffset) * -1;

            if (leftPos < leftPosMax) {
                my.logger.info('Correcting leftPos', {leftPosMax: leftPosMax});
                leftPos = leftPosMax;
            }
            my.$strip.animate({left: leftPos}, 400);

            setButtonState(my.buttons.previous, spec.firstItem != 0);
            setButtonState(my.buttons.next, spec.firstItem < my.maxItem);
        };

        var next = function () {
            my.logger.debug('next()');
            spec.firstItem += spec.navigationBatchSize;

            if (spec.firstItem > my.maxItem) {
                my.logger.info('Reached the last item');
                spec.firstItem = my.maxItem;
                autoNavStop();
            }
            draw();
        };

        var previous = function () {
            my.logger.debug('previous()');
            spec.firstItem -= spec.navigationBatchSize;

            if (spec.firstItem < 0) {
                my.logger.info('Reached the first item');
                spec.firstItem = 0;
                autoNavStop();
            }
            draw();
        };

        var setButtonState = function (button, enableCondition) {
            my.logger.debug('setButtonState()', button, enableCondition);
            button[(enableCondition ? 'remove' : 'add') + 'Class']('disabled');
        };

        var autoNavStart = function (func) {
            my.logger.debug('autoNavStart()');
            func();
            if (my.navigationInterval) {
                window.clearInterval(my.navigationInterval);
            }
            my.navigationInterval = window.setInterval(func, spec.autoNavSpeed);
        };

        var autoNavStop = function () {
            my.logger.debug('autoNavStop()');
            if (!my.navigationInterval) {
                return;
            }
            window.clearInterval(my.navigationInterval);
            my.navigationInterval = null;
        };

        // ------------------------------------------------------------------
        // Public methods
        // ------------------------------------------------------------------

        that.previous = previous;
        that.next = next;

        // ------------------------------------------------------------------
        // Constructor
        // ------------------------------------------------------------------

        return init();
    };

})(window.cmu);

