/*global jQuery */

(function ($) {
    
    var Carousel;

    /*
        Properties:
        - frame
        - stage
        - slides
        - currentSlideIndex
        - origin
        - speed
        - wrap
        - intval
        - duration
        
        Methods:
        - select()
        - advance()
        - next()
        - prev()
        - first()
        - last()
        - isPlaying()
        - start()
        - stop()
        - isFirst()
        - isLast()
     */
    
    Carousel = function (frame, userOptions) {
           
        var options, that;
        
        // Merge the user's options with the defaults.
        options = $.extend({}, Carousel.defaultOptions, userOptions);
        
        // Set the major players.        
        this.frame = frame;
        this.stage = options.stage;
        this.slides = options.slides;
        
        // Store a reference to this.
        this.frame.data("Carousel", this);
        
        // Set the origin
        
        // User supplied an explicit origin.
        if (typeof userOptions.origin === "object" && 
                typeof userOptions.origin.top === "number" &&
                typeof userOptions.origin.left === "number") {
            
            this.origin = userOptions.origin;
        } 
        
        // Infer the origin from the location of the stage.
        else {
            
            this.origin = this.stage.position();
            
            // Cast the results as ints
            this.origin.left = Math.round(this.origin.left);
            this.origin.top = Math.round(this.origin.top);
        }        
        
        // Simple options
        this.duration = options.duration;
        this.speed = options.speed;
        this.wrap = options.wrap;
        this.slidesPadEnd = options.slidesPadEnd;
        
        // Members always initialized to specific states.
        this.currentSlideIndex = 0;
        this.intval = null;
        
        if (options.autoStart) {
            
            if (options.delay) {

                that = this;

                setTimeout(function () {
                    that.start();
                }, options.delay);

            }
            else {    
                this.start();
            }
        
        }
        
    };  



    Carousel.prototype = {
        
        slideCount: function () {
            return this.slides.length - this.slidesPadEnd;
        },
        
        // Move a specific slide into view.
        // Update the currentSlideIndex member.
        select: function (slideIndex) {
            
            var slide, pos;
            
            // Do nothing if the "new" slide is the current slide.
            if (slideIndex === this.currentSlideIndex) {
                return;
            }
                            
            // Determine the new position for the stage. This is the inverse of
            // the slide's offset position adjusted for the origin.
            slide = $(this.slides[slideIndex]);
            pos = slide.position();
            
            // FF Mac returns floats instead of ints, we may need to correct.
            pos.left = -(Math.round(pos.left)) + this.origin.left;
            pos.top = -(Math.round(pos.top)) + this.origin.top;
            
            // Perform the animation
            this.stage.animate({
                top: pos.top + "px",
                left: pos.left + "px"    
            }, this.speed);
            
            // Update the slide index.
            this.currentSlideIndex = slideIndex;
            
            // Call the event.
            this.frame.trigger("change");

        },

        // Move forward the specified number of slides.
        advance: function (delta) {
            
            var newSlideIndex, slideCount;
            
            // Default value moves forward one slide.
            delta = delta || 1;

            slideCount = this.slideCount();
            
            newSlideIndex = this.currentSlideIndex + delta;

            // Ensure the new slide is in range.
            if (newSlideIndex < 0) {
                if (this.wrap) {
                    newSlideIndex = slideCount;
                }
                else {
                    return;
                }    
            }
            else if (newSlideIndex > slideCount) {
                if (this.wrap) {
                    newSlideIndex = 0;
                }
                else {
                    return;
                }    
            }
                    
            // Advance to the new slide.
            this.select(newSlideIndex);   

        },

        // Move the next slide into view.
        next: function () {
            this.advance(1);
        },
        
        // Move the previous slide into view.
        previous: function (delta) {
            this.advance(-1);
        },
        
        // Advance to the first slide.        
        first: function () {
            this.select(0);
        },
        
        // Advnace to th final slide.
        last: function () {
            this.select(this.slideCount() - 1);
        },        
        
        // Return if the carousel is auto-advancing.
        isPlaying: function () {
            return (this.intval !== null);
        },
        
        // Start the carousel auto-advancing.
        start: function () {
            if (!this.isPlaying()) {
                this.next();
                this.startTimer();         
            } 
        },
        
        // Stop the carousel auto-advancing.
        stop: function () {
            clearInterval(this.intval);
            this.intval = null;
        },
        
        // Setup next() on an interval.
        startTimer: function () {
            var that = this;
            this.intval = setInterval(function () {
                that.next();
            }, this.duration);
        },
        
        // The carousel is at the first slide
        isFirst: function () {
            return this.currentSlideIndex === 0;
        },
        
        // The carousel is at the ast slide
        isLast: function () {
            return this.currentSlideIndex === this.slideCount();
        }

    };
    
    
    
    Carousel.defaultOptions = {
        delay: 0,
        duration: 2000,
        slidesPadEnd: 0,
        speed: 500,
        wrap: true        
    };



    // -------------------------------------------------------------------------
    // Extend jQuery 

    if (typeof $.fn.carousel === "undefined") {

        $.fn.carousel = function () {

            var method, options, rtn;

            if (typeof arguments[0] !== "string") {
                method = "create";
                options = arguments[0];
            }
            else {
                method = arguments[0];
                options = arguments[1];
            }
            
            this.each(function () {
            
                var carouselObj;
                
                carouselObj = $(this).data("Carousel");
                
                // No Carousel setup yet. Let's create one!
                if (carouselObj === null || typeof carouselObj === "undefined") {
                    carouselObj = new Carousel($(this), options);
                    return true;
                }
                
                // Invoke a method.
                switch (method) {
                case "select":
                    carouselObj.select(parseInt(options, 10));
                    break;
                
                case "advance":
                    carouselObj.advance(options);
                    break;
                    
                case "next":
                    carouselObj.next();
                    break;
                    
                case "prev":
                case "previous":
                    carouselObj.previous();
                    break;

                case "first":
                    carouselObj.first();
                    break;
                    
                case "last":
                    carouselObj.last();
                    break;

                case "start":
                    carouselObj.start();
                    break;
                    
                case "stop":
                    carouselObj.stop();
                    break;
                    
                case "getIndex":
                    rtn = carouselObj.currentSlideIndex;
                    return false;
                    
                case "slideCount":
                    rtn = carouselObj.slideCount();
                    return false;    
                    
                case "isFirst":
                    rtn = carouselObj.isFirst();
                    return false;
                    
                case "isLast":
                    rtn = carouselObj.isLast();
                    return false;
                }

            });

            if (typeof rtn === "undefined") {
                return this;
            }
            else {
                return rtn;
            }
            
        };

    }      

}(jQuery));

/*jslint white: true, onevar: true, browser: true, undef: true, nomen: true,
  eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true,
  immed: true */