/**
 * Created by IntelliJ IDEA.
 * User: mcanas
 * Date: Oct 11, 2010
 * Time: 1:38:22 PM
 * To change this template use File | Settings | File Templates.
 */
(function($){

    // Define the public methods    
    var methods = {
        /* The initializer method
         *  Called automatically when no registered methods are passed to jSelect();
         * */
        init : function( options ) {
            return this.each(function() {

                var $this = $(this),
                    props = $this.data('properties');

                // If the object has not been initialized...
                if(!props) {

                    // Set the default settings values
                    var settings = {
                        autoPlay: false,
                        axis : 'horizontal',
                        collapse : false,
                        delay : 3,
                        easing : 'swing',
                        looping : false,
                        speed : 1,
                        onAnimationStart : null,
                        onAnimationEnd : null
                    };

                    // Replace the default settings with the user settings
                    if( options ) { $.extend(settings, options) }

                    // Grab the DOM components
                    var $screen = $('.jShow_screen', $this); // The screen is the viewable area of the jShow slider
                    var $reel = $('.jShow_reel', $screen); // The reel is the object that will be animated

                    if(settings.looping) { $reel.append($reel.html()); }

                    var $frames = $.map($('.jShow_frame', $reel), function(obj) { return $(obj); }); // A js array of jquery objects containing each frame
                    var nav = { // jShow navigational elements
                        next : $('.jShow_nav_next', $this), // the next frame button
                        play : $('.jShow_nav_play', $this), // the play button, which will be toggled to represent the pause button
                        prev : $('.jShow_nav_prev', $this), // the previous frame button
                        to : $.map($('.jShow_nav_to', $this), function(obj) { return $(obj); }) // the skip to frame buttons. The frame to skip to is determined by their order.
                    };

                    // Collect all of the properties, settings and DOM components into one namespace
                    $this.data('properties', {
                        animating : false,
                        axial : {
                            dimension : ((settings.axis == 'vertical') ? 'outerHeight' : 'outerWidth'),
                            position : ((settings.axis == 'vertical') ? 'top' : 'left')
                        },
                        frames : $frames,
                        frameCount : (settings.looping) ? ($frames.length/2) : $frames.length,
                        index : { current: 0, selected: 0 },
                        interval : 0,
                        nav : nav,
                        paused : true,
                        reel : $reel,
                        screen : $screen,
                        settings : settings
                    });

                    props = $this.data('properties');

                    var reelHeight = $reel.height(), reelWidth = $reel.width();

                    // Arrange the frames as necessary, based on the axis setting, and re-size the reel
                    $.each($frames, function(i, $thisFrame){

                        $thisFrame.data('properties', {
                            index : {
                                relative : ((i >= $frames.length/2 && settings.looping) ?  i - ($frames.length/2) : i),
                                absolute : i
                            }
                        });

                        if(i != props.index.current) {
                            if(settings.axis == 'vertical') {
                                reelHeight += $thisFrame[props.axial.dimension]();
                            } else {
                                $thisFrame.css('float','left');
                                reelWidth += $thisFrame[props.axial.dimension]();
                            }
                        }

                        if(i == ($frames.length-1)) {
                            if(settings.axis == 'vertical') {
                                reelWidth = props.frames[0].outerWidth();
                            } else {
                                $frames[props.index.current].css('float','left');
                                reelHeight = props.frames[0].outerHeight();
                            }

                            $reel.css({ height:reelHeight, width:reelWidth });
                        }
                    });

                    // Attach the event handler for the skip to frame buttons
                    if(props.nav.to.length > 0) {
                        $.each(props.nav.to, function(i, navTo) {
                            navTo.bind('click', function(e) {
                                e.preventDefault();

                                if(!props.paused) pause($this);
                                skipToFrame($this, i, 'to');
                            });
                        });
                    }

                    // Attach the event handler for the next frame button
                    if(props.nav.next.length > 0) {
                        $.each(props.nav.next, function(i, obj) {
                            $(obj).bind('click', function(e) {
                                e.preventDefault();

                                if(!props.paused) pause($this);
                                nextFrame($this);
                            });
                        });
                    }

                    // Attach the event handler for the next frame button
                    if(props.nav.prev.length > 0) {
                        $.each(props.nav.prev, function(i, obj) {
                            $(obj).bind('click', function(e) {
                                e.preventDefault();

                                if(!props.paused) pause($this);
                                prevFrame($this);
                            });
                        });
                    }                                     

                    // Attach the event handler for the play/pause button
                    if(props.nav.play.length > 0) {
                        $.each(props.nav.play, function(i, obj) {
                            $(obj).bind('click', function(e) {
                                e.preventDefault();

                                if(props.paused) play($this);
                                else pause($this);
                            });
                        });
                    }

                    if(settings.delay > 0 && settings.autoPlay) { play($this); } // If the show delay is greater than 0 and autoPlay is true, start the slide show
                    else { props.nav.play.addClass('paused'); } // Otherwise set the play button to pause
                }
            });
        },
        next : function() { // Next frame public function
            nextFrame(this);
        },
        pause : function() { // Pause slide show public function
            pause(this);
        },
        play : function() { // Play slide show public function
            play(this);
        },
        previous : function() { // Next frame public function
            prevFrame(this);
        },
        skipTo : function(i) { // Skip to frame public function
            skipToFrame(this, i, 'to');
        }
    };

    // Next frame private function
    function nextFrame( $this ) {
        var props = $this.data('properties');
        var selectedIndex = (props.index.current == (props.frames.length-1)) ? 0 : props.index.current + 1;
        skipToFrame($this, selectedIndex, 'next');
    }

    // Pause slide show private function
    function pause( $this ) {
        var props = $this.data('properties');
        window.clearInterval(props.interval);
        props.paused = true;
        props.nav.play.addClass('paused');
    }    

    // Play slide show private function
    function play( $this ) {
        var props = $this.data('properties');
        props.interval = window.setInterval(function() {
            nextFrame($this);
        }, props.settings.delay * 1000);
        props.paused = false;
        props.nav.play.removeClass('paused');        
    }

    // Previous frame private function
    function prevFrame( $this ) {
        var props = $this.data('properties');
        var selectedIndex = (props.index.current == 0) ? props.frames.length - 1 : props.index.current - 1;
        skipToFrame($this, selectedIndex, 'previous');
    }

    // Skip to frame private function
    function skipToFrame($this, selectedIndex, direction) {
         // Grab the properties
        var props = $this.data('properties');

        // If the slider is animating, DIE!!!
        if(props.animating || props.index.current == selectedIndex) { return false; }

        var frameMoveCount = 0, moveDistance = 0;

        // Handle movement and index scenarios related to the looping functionality
        if(props.settings.looping) {
            if(props.index.current >= (props.frameCount) && direction == 'to') {
                props.index.selected = selectedIndex + (props.frameCount);
            } else if(props.index.current == (props.frames.length-1) && direction == 'next') { // If you're on the last frame, handle the next event
                moveDistance = props.frames[props.frames[props.index.current].data('properties').index.relative].position()[props.axial.position] * -1;
                props.reel.css(props.axial.position, moveDistance);

                props.index.current = props.frames[props.index.current].data('properties').index.relative;
                props.index.selected = props.index.current+1;
            } else if (props.index.current == 0 && direction == 'previous') { // If you're on the first frame, handle the previous event
                moveDistance = props.frames[props.frameCount].position()[props.axial.position] * -1;
                props.reel.css(props.axial.position, moveDistance);

                props.index.current = props.frames[props.frameCount].data('properties').index.absolute;
                props.index.selected = props.index.current-1;                
            } else {
                props.index.selected = selectedIndex;
            }
        } else {
            props.index.selected = selectedIndex;
        }

         // Set the active Skip To nav element
        if(props.nav.to.length > 0) {
            props.nav.to[props.frames[props.index.current].data('properties').index.relative].removeClass('active');
            props.nav.to[props.frames[props.index.selected].data('properties').index.relative].addClass('active');
        }

        // Calculate the number of frames to move by
        frameMoveCount = props.index.selected - props.index.current;

        // If collapse is set, and the move count is greater than 1...
        if(props.settings.collapse && Math.abs(frameMoveCount) > 1) {

            // Collapse the intermediate frames
            $.each(props.frames, function(j, $thisFrame) {
                if(frameMoveCount > 0 && j > props.index.current && j < props.index.selected) {
                    $thisFrame.addClass('collapsed');
                } else if(frameMoveCount < 0 && j < props.index.current && j > props.index.selected) {
                    props.reel.css(props.axial.position, (props.reel.position()[props.axial.position] + $thisFrame[props.axial.dimension]()));
                    $thisFrame.addClass('collapsed');
                }
            });
        }

        // Calculate the reel move distance
        moveDistance = props.frames[props.index.selected].position()[props.axial.position] * -1;

        // Set the jquery animation object settings based on the jShow axis value
        var anim = (props.settings.axis == 'vertical') ? {top:moveDistance} : {left:moveDistance};

        // We're animating!... Almost
        props.animating = true;

        // If there's a user set onAnimationStart function, call it
        if(props.settings.onAnimationStart != null) {
            props.settings.onAnimationStart.call($this);
        }

        // Now we're animating!
        props.reel.animate(anim, props.settings.speed*1000, props.settings.easing, function() {

            // When the animation is finished, uncollapse the collapsed frames
            if(props.settings.collapse && Math.abs(frameMoveCount) > 1) {
                $('.collapsed', props.reel).each(function() {
                    var $thisFrame = $(this);
                    $thisFrame.removeClass('collapsed');

                    if(frameMoveCount > 0) {
                        props.reel.css(props.axial.position, (props.reel.position()[props.axial.position] - $thisFrame[props.axial.dimension]()));
                    }
                });
            }

            // We're not animating anymore
            props.animating = false;

            // If there's a user set onAnimationEnd function, call it
            if(props.settings.onAnimationEnd != null) {
                props.settings.onAnimationEnd.call($this);
            }
        });

        // Set the current index to the selected index
        props.index.current = props.index.selected;
        return true;
    }
    
    $.fn.jShow = function( method ) {
        if(methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if(typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method '+method+' does not exist on jQuery.jSelect');
        }
    };
})(jQuery);
