/**
* author Remy Sharp
* url http://remysharp.com/tag/rotator
*/

(function ($) {
    $.fn.rotator = function (klass) {
        var newrotator = [],
            last = this.length;
		var id =  '#'+jQuery( this ).attr( 'id' ) ;

		// works out the left or right hand reset position, based on scroll
        // behavior, current direction and new direction
        function getReset(newDir, rotatorRedux, rotatorState) {
            var behavior = rotatorState.behavior, width = rotatorState.width, dir = rotatorState.dir;
            var r = 0;
            if (behavior == 'alternate') {
                r = newDir == 1 ? rotatorRedux[rotatorState.widthAxis] - (width*2) : width;
            } else if (behavior == 'slide') {
                if (newDir == -1) {
                    r = dir == -1 ? rotatorRedux[rotatorState.widthAxis] : width;
                } else {
                    r = dir == -1 ? rotatorRedux[rotatorState.widthAxis] - (width*2) : 0;
                }
            } else {
                r = newDir == -1 ? rotatorRedux[rotatorState.widthAxis] : 0;
            }
            return r;
        }
		
        // single "thread" animation
        function animaterotator() {
        	var i = newrotator.length,
                rotatorRedux = null,
                $rotatorRedux = null,
                rotatorState = {},
                newrotatorList = [],
                hitedge = false;
          	
            while (i--) {
                rotatorRedux = newrotator[i];
                $rotatorRedux = $(rotatorRedux);                
                rotatorState = $rotatorRedux.data('rotatorState');
                
                if ($rotatorRedux.data('paused') !== true) {
                    // TODO read scrollamount, dir, behavior, loops and last from data
                    rotatorRedux[rotatorState.axis] += (rotatorState.scrollamount * rotatorState.dir);
                    
                    // only true if it's hit the end
                    hitedge = rotatorState.dir == -1 ? rotatorRedux[rotatorState.axis] <= getReset(rotatorState.dir * -1, rotatorRedux, rotatorState) : rotatorRedux[rotatorState.axis] >= getReset(rotatorState.dir * -1, rotatorRedux, rotatorState);
                    
                    if ((rotatorState.behavior == 'scroll' && rotatorState.last == rotatorRedux[rotatorState.axis]) || (rotatorState.behavior == 'alternate' && hitedge && rotatorState.last != -1) || (rotatorState.behavior == 'slide' && hitedge && rotatorState.last != -1)) {                        
                        if (rotatorState.behavior == 'alternate') {
                            rotatorState.dir *= -1; // flip
                        }
                        rotatorState.last = -1;

                        $rotatorRedux.trigger('stop');

                        rotatorState.loops--;
                        if (rotatorState.loops === 0) {
                            if (rotatorState.behavior != 'slide') {
                                rotatorRedux[rotatorState.axis] = getReset(rotatorState.dir, rotatorRedux, rotatorState);
                            } else {
                                // corrects the position
                                rotatorRedux[rotatorState.axis] = getReset(rotatorState.dir * -1, rotatorRedux, rotatorState);
                            }

                            $rotatorRedux.trigger('end');
                        } else {
                            // keep this rotator going
                            newrotatorList.push(rotatorRedux);
                            $rotatorRedux.trigger('start');
                            rotatorRedux[rotatorState.axis] = getReset(rotatorState.dir, rotatorRedux, rotatorState);
                        }
                    } else {
                        newrotatorList.push(rotatorRedux);
                    }
                    rotatorState.last = rotatorRedux[rotatorState.axis];

                    // store updated state only if we ran an animation
                    $rotatorRedux.data('rotatorState', rotatorState);
                } else {
                    // even though it's paused, keep it in the list
                    newrotatorList.push(rotatorRedux);                    
                }
            }

            newrotator = newrotatorList;
            
            if (newrotator.length) {
                setTimeout(animaterotator, 25);
            }            
        }
        
        // TODO consider whether using .html() in the wrapping process could lead to loosing predefined events...
        this.each(function (i) {
            var $rotator = $(this),
                width = $rotator.attr('width') || $rotator.width(),
                height = $rotator.attr('height') || $rotator.height(),
                $rotatorRedux = $rotator.after('<div ' + (klass ? 'class="' + klass + '" ' : '') + 'style="display: block-inline; width: ' + width + 'px; height: ' + height + 'px; overflow: hidden;"><div style="float: left; white-space: nowrap;">' + $rotator.html() + '</div></div>').next(),
                rotatorRedux = $rotatorRedux.get(0),
                hitedge = 0,
                direction = ($rotator.attr('direction') || 'left').toLowerCase(),
                rotatorState = {
                    dir : /down|right/.test(direction) ? -1 : 1,
                    axis : /left|right/.test(direction) ? 'scrollLeft' : 'scrollTop',
                    widthAxis : /left|right/.test(direction) ? 'scrollWidth' : 'scrollHeight',
                    last : -1,
                    loops : $rotator.attr('loop') || -1,
                    scrollamount : $rotator.attr('scrollamount') || this.scrollAmount || 2,
                    behavior : ($rotator.attr('behavior') || 'scroll').toLowerCase(),
                    width : /left|right/.test(direction) ? width : height
                };
            // corrects a bug in Firefox - the default loops for slide is -1
            if ($rotator.attr('loop') == -1 && rotatorState.behavior == 'slide') {
                rotatorState.loops = 1;
            }

            $rotator.remove();
            
            // add padding
            if (/left|right/.test(direction)) {
                $rotatorRedux.find('> div').css('padding', '0 ' + width + 'px');
            } else {
                $rotatorRedux.find('> div').css('padding', height + 'px 0');
            }
            
            // events
            $rotatorRedux.bind('stop', function () {
                $rotatorRedux.data('paused', true);
            }).bind('pause', function () {
                $rotatorRedux.data('paused', true);
            }).bind('start', function () {
                $rotatorRedux.data('paused', false);
            }).bind('unpause', function () {
                $rotatorRedux.data('paused', false);
            }).data('rotatorState', rotatorState); // finally: store the state
            
            // todo - rerender event allowing us to do an ajax hit and redraw the rotator

            newrotator.push(rotatorRedux);

            rotatorRedux[rotatorState.axis] = getReset(rotatorState.dir, rotatorRedux, rotatorState);
            $rotatorRedux.trigger('start');
            
            // on the very last rotator, trigger the animation
            if (i+1 == last) {            	
                animaterotator();
            }
        });            

        return $(newrotator);
    };
}(jQuery));

