Pubsmith.namespace("plugins.imagerotator");

Pubsmith.plugins.imagerotator = function (userOptions) {
    var defaultOptions,
        options,
        $instance,
        $itemList,
        $items,
        $navigation,
        playlistInstance,
        $currentItem = null,
        currentItemId = false,
        playing = false,
        playingTimer = null,
        dirty = false; // track whether or not tweening is taking place

    defaultOptions = {
        elem: null,
        autoPlay: true,
        speed: 5000,
        tweenType: 'fade',
        tweenDuration: 1000
    };

    options = $.extend(true, {}, defaultOptions, userOptions);

    var construct = function() {
        if(!options.elem) {
            return false;
        }

        // $playerInstance is to be a jQuery var, if options.elem isn't, make it so.
        $instance = options.elem instanceof jQuery ? options.elem : $(options.elem);

        // validate options
        if(options.speed < options.tweenDuration) { // speed can't be less than tween duration
            options.speed = options.tweenDuration
        }

        initializeItems();

        $navigation = $instance.find('ol.navigation');

        if($navigation) {
            initializeNavigation();
        }

        if(options.autoPlay) {
            start();
        }
    }

    var initializeItems = function () {
        $itemList = $instance.find('ol').eq(0);
        $items = $itemList.find('> li');

        currentItemId = 0;
        $currentItem = $items.eq(currentItemId);
        $currentItem.css('z-index', 10);

        switch(options.tweenType) {
            case 'scroll':
                $itemList.prepend($items.last().clone());
                $itemList.prepend($items.eq($items.size()-2).clone());
                $itemList.append($items.first().clone());
                $itemList.append($items.eq(1).clone());

                $itemList.css('left', -$items.innerWidth()*2 + 'px');
                var $domItems = $itemList.find('> li');

                // set ol elem to width of all items
                $itemList.css('width', $domItems.innerWidth()*$domItems.size() + 'px');
                $domItems.each(function(){
                    $(this).css({
                        'float': 'left'
                    });
                });
                break;

            case 'fade':
                $items.each(function(){
                    $(this).css('z-index', 5);
                });
                $items.eq(0).css('z-index', 10);
                break;
        }
    }

    var initializeNavigation = function () {
        $navigation.find('li a').click(function(e) {
            e.preventDefault();

            var id = $(this).parent().attr('class').split('-')[1];
            if(id == 'previous') {
                gotoPreviousItem();
            }
            else if(id == 'next') {
                gotoNextItem();
            }
            else {
                gotoItem(parseInt(id)-1);
            }
        });

        $navigation.find('li.nav-'+(currentItemId+1)).addClass('selected');
    }

    var start = function () {
        playingTimer = setTimeout(function () {
            gotoNextItem();
        }, options.speed);
    }

    var stop = function () {

    }

    var gotoNextItem = function () {
        gotoItem($items.size() - 1 == currentItemId ? 0 : currentItemId + 1);
    }

    var gotoPreviousItem = function () {
        gotoItem(currentItemId == 0 ? $items.size() - 1 : currentItemId - 1, 'previous');
    }

    var gotoItem = function (destinationId, direction) {
        if(dirty) {
            return false;
        }

        startTween($items.eq(currentItemId), $items.eq(destinationId), destinationId, direction)
    }

    var startTween = function ($from, $to, destinationId, direction) {
        // don't tween if in the middle of a tween
        if(destinationId == currentItemId) {
            return false;
        }

        // select nav item for destination item
        $navigation.find('li').removeClass('selected');
        $navigation.find('li.nav-'+(destinationId+1)).addClass('selected');

        clearInterval(playingTimer);

        // console.log(currentItemId + ' -> ' + destinationId);
        var type, duration;

        type = options.tweenType;
        duration = options.tweenDuration;

        dirty = true;

        switch(type) {
            case 'fade':
                $to.css({
                    'z-index': 9,
                    'opacity': 100,
                    'position': 'absolute',
                    'top': 0
                });

                $from.fadeTo(duration, 0, function () {
                    $to.css('z-index', 10);
                    $from.css('z-index', 5);
                    completeTween($to);
                });

                break;

            case 'scroll':
                var direction = direction == 'previous' ? '+' : '-';

                var itemsToPass;

                // determine how many items we are away from destination
                // useful for making sure we have enough in front of us to animate
                if(destinationId < currentItemId) {
                    itemsToPass = ($items.size() - currentItemId) + destinationId;
                }
                else {
                    itemsToPass = Math.abs(destinationId - currentItemId);
                }

                var $domItems = $itemList.find('> li');

                // find the index of the current image in the dom
                var count = 0;
                var currentDomIdx = null;
                $domItems.each(function(){
                    if($(this).is($currentItem)) {
                        currentDomIdx = count;
                    }
                    count++;
                });

                // determine if sizes to fly pass are available at end of stack
                if($domItems.size() < (currentDomIdx + 1) + itemsToPass) {

                    // subtract 1 from currentDomIdx to get the range of indexes for items
                    var leftoversFromEnd = $items.size() - (currentDomIdx - 1);

                    // get from end of stack
                    var itemsToDupe = $domItems.slice(currentDomIdx, currentDomIdx + leftoversFromEnd);

                    // not enough at end of stack, need to pull from front
                    if(itemsToDupe.size() < itemsToPass) {
                        // 2 * 2 cause 2 items padded on front and end of stack
                        $.merge(itemsToDupe, $domItems.slice(2 * 2, 2 + itemsToPass - itemsToDupe.size()));
                    }

                    // add all extra items to the dom
                    // TODO -- do this in one append for better performance
                    itemsToDupe.each(function () {
                        $itemList.append($(this).clone());
                    });

                    // set ol elem to width of all items
                    $itemList.css('width', $domItems.innerWidth()*$itemList.find('li').size() + 'px');
                }

                var destPos = itemsToPass * $to.innerWidth();

                $itemList.animate({
                    'left': direction+'='+destPos
                }, duration, function () {
                    var destLeft = '-'+(destinationId+2)*$to.innerWidth();

                    $itemList.css('left', destLeft+'px');

                    completeTween($to);
                });

                break;
        }

        currentItemId = destinationId;
    }

    var completeTween = function ($newItem) {
        dirty = false;
        $currentItem = $newItem;
        start();
    }

    construct();
}
