/**
* author Remy Sharp
* url http://remysharp.com/tag/ticker
*/

(function ($) {
    $.fn.ticker = function (klass) {
        var newMarquee = [],
            last = this.length;

        // works out the left or right hand reset position, based on scroll
        // behavior, current direction and new direction
        function getReset(newDir, tickerRedux, tickerState) {
            var behavior = tickerState.behavior, width = tickerState.width, dir = tickerState.dir;
            var r = 0;
            if (behavior == 'alternate') {
                r = newDir == 1 ? tickerRedux[tickerState.widthAxis] - (width*2) : width;
            } else if (behavior == 'slide') {
                if (newDir == -1) {
                    r = dir == -1 ? tickerRedux[tickerState.widthAxis] : width;
                } else {
                    r = dir == -1 ? tickerRedux[tickerState.widthAxis] - (width*2) : 0;
                }
            } else {
                r = newDir == -1 ? tickerRedux[tickerState.widthAxis] : 0;
            }
            return r;
        }

        // single "thread" animation
        function animateMarquee() {
            var i = newMarquee.length,
                tickerRedux = null,
                $tickerRedux = null,
                tickerState = {},
                newMarqueeList = [],
                hitedge = false;
                
            while (i--) {
                tickerRedux = newMarquee[i];
                $tickerRedux = $(tickerRedux);
                tickerState = $tickerRedux.data('tickerState');
                
                if ($tickerRedux.data('paused') !== true) {
                    // TODO read scrollamount, dir, behavior, loops and last from data
                    tickerRedux[tickerState.axis] += (tickerState.scrollamount * tickerState.dir);

                    // only true if it's hit the end
                    hitedge = tickerState.dir == -1 ? tickerRedux[tickerState.axis] <= getReset(tickerState.dir * -1, tickerRedux, tickerState) : tickerRedux[tickerState.axis] >= getReset(tickerState.dir * -1, tickerRedux, tickerState);
                    
                    if ((tickerState.behavior == 'scroll' && tickerState.last == tickerRedux[tickerState.axis]) || (tickerState.behavior == 'alternate' && hitedge && tickerState.last != -1) || (tickerState.behavior == 'slide' && hitedge && tickerState.last != -1)) {                        
                        if (tickerState.behavior == 'alternate') {
                            tickerState.dir *= -1; // flip
                        }
                        tickerState.last = -1;

                        $tickerRedux.trigger('stop');

                        tickerState.loops--;
                        if (tickerState.loops === 0) {
                            if (tickerState.behavior != 'slide') {
                                tickerRedux[tickerState.axis] = getReset(tickerState.dir, tickerRedux, tickerState);
                            } else {
                                // corrects the position
                                tickerRedux[tickerState.axis] = getReset(tickerState.dir * -1, tickerRedux, tickerState);
                            }

                            $tickerRedux.trigger('end');
                        } else {
                            // keep this ticker going
                            newMarqueeList.push(tickerRedux);
                            $tickerRedux.trigger('start');
                            tickerRedux[tickerState.axis] = getReset(tickerState.dir, tickerRedux, tickerState);
                        }
                    } else {
                        newMarqueeList.push(tickerRedux);
                    }
                    tickerState.last = tickerRedux[tickerState.axis];

                    // store updated state only if we ran an animation
                    $tickerRedux.data('tickerState', tickerState);
                } else {
                    // even though it's paused, keep it in the list
                    newMarqueeList.push(tickerRedux);                    
                }
            }

            newMarquee = newMarqueeList;
            
            if (newMarquee.length) {
                setTimeout(animateMarquee, 25);
            }            
        }
        
        // TODO consider whether using .html() in the wrapping process could lead to loosing predefined events...
        this.each(function (i) {
            var $ticker = $(this),
                width = $ticker.attr('width') || $ticker.width(),
                height = $ticker.attr('height') || $ticker.height(),
                $tickerRedux = $ticker.after('<div ' + (klass ? 'class="' + klass + '" ' : '') + 'style="display: block-inline; width: ' + width + 'px; height: ' + height + 'px; overflow: hidden;"><div style="float: left; white-space: nowrap;">' + $ticker.html() + '</div></div>').next(),
                tickerRedux = $tickerRedux.get(0),
                hitedge = 0,
                direction = ($ticker.attr('direction') || 'left').toLowerCase(),
                tickerState = {
                    dir : /down|right/.test(direction) ? -1 : 1,
                    axis : /left|right/.test(direction) ? 'scrollLeft' : 'scrollTop',
                    widthAxis : /left|right/.test(direction) ? 'scrollWidth' : 'scrollHeight',
                    last : -1,
                    loops : $ticker.attr('loop') || -1,
                    scrollamount : $ticker.attr('scrollamount') || this.scrollAmount || 2,
                    behavior : ($ticker.attr('behavior') || 'scroll').toLowerCase(),
                    width : /left|right/.test(direction) ? width : height
                };
            
            // corrects a bug in Firefox - the default loops for slide is -1
            if ($ticker.attr('loop') == -1 && tickerState.behavior == 'slide') {
                tickerState.loops = 1;
            }

            $ticker.remove();
            
            // add padding
            if (/left|right/.test(direction)) {
                $tickerRedux.find('> div').css('padding', '0 ' + width + 'px');
            } else {
                $tickerRedux.find('> div').css('padding', height + 'px 0');
            }
            
            // events
            $tickerRedux.bind('stop', function () {
                $tickerRedux.data('paused', true);
            }).bind('pause', function () {
                $tickerRedux.data('paused', true);
            }).bind('start', function () {
                $tickerRedux.data('paused', false);
            }).bind('unpause', function () {
                $tickerRedux.data('paused', false);
            }).data('tickerState', tickerState); // finally: store the state
            
            // todo - rerender event allowing us to do an ajax hit and redraw the ticker

            newMarquee.push(tickerRedux);

            tickerRedux[tickerState.axis] = getReset(tickerState.dir, tickerRedux, tickerState);
            $tickerRedux.trigger('start');
            
            // on the very last ticker, trigger the animation
            if (i+1 == last) {
                animateMarquee();
            }
        });            

        return $(newMarquee);
    };
}(jQuery));

