/**
 * jCarousel - Riding carousels with jQuery
 *
 * Written by Jan Sorgalla
 *   http://sorgalla.com
 *
 * Licensed under the MIT License
 *   http://www.opensource.org/licenses/mit-license.php
 *
 * Built on top of the jQuery library
 *   http://jquery.com
 *
 * Inspired by the "Carousel Component" by Bill Scott
 *   http://billwscott.com/carousel/
 */

jQuery.fn.extend({
    jcarousel: function(o) {
        return this.each(function() {
            new jQuery.jcarousel(this, o);
        });
    }
});

jQuery.jcarousel = function(c, o) {
    this.init(c, o);
};

jQuery.jcarousel.prototype = {

    init: function(c, o)
    {
        this.c = c;

        this.horiz = o.orientation == "vertical" ? false : true;

        this.itemW = o.itemWidth || 100;
        this.itemH = o.itemHeight || 100;

        this.visible = o.itemVisible || 3;
        this.step    = o.itemScroll || this.visible;

        this.ani = o.scrollAnimation == undefined ? "fast" : o.scrollAnimation;

        this.autoD   = o.autoScroll || 0;
        this.autoT   = null;
        this.autoSOI = o.autoScrollStopOnInteract || true;

        this.wrap = o.wrap ? o.wrap : false;

        this.loadHandler = o.loadItemHandler || null;
        this.nextHandler = o.nextButtonStateHandler || null;
        this.prevHandler = o.prevButtonStateHandler || null;

        this.first = 1;
        this.last  = 1;

        this.list = jQuery(".jcarousel-list", this.c).get(0);

        this.size = jQuery("li", this.list).size();
        this.end = this.size;

        if (this.horiz) {
            this.itemD = this.itemW;
            var clipW  = this.itemW * this.visible;
            var clipH  = this.itemH;
        } else {
            this.itemD = this.itemH;
            var clipW  = this.itemW;
            var clipH  = this.itemH * this.visible;
        }

        jQuery(".jcarousel-clip", this.c).css({
            'z-index': 3,
            'padding': 0,
            //'margin': 0,
            'width':  clipW + 'px',
            'height': clipH + 'px',
            'overflow': 'hidden',
            'position': 'relative'
        });

        this.top  = this.intval(jQuery(this.list).css('top'));
        this.left = this.intval(jQuery(this.list).css('left'));

        jQuery(this.list).css({
            // "top" and "left" must be set, jQuery.animate() fails otherwise on first animation
            'z-index': 1,
            'position': 'relative',
            'top': this.top,
            'left': this.left,
            'margin': 0,
            'padding': 0,
            'z-index': 1
        });

        if (this.size > 0) {
            this.resize();

            var idx = 1;

            jQuery("li", this.list).css({
                'float': 'left',
                'styleFloat': 'left',
                'width':  this.itemW + 'px',
                'height': this.itemH + 'px',
                'overflow': 'hidden',
                'list-style': 'none'
            }).each(function() {
                jQuery(this).addClass(".jcarousel-item-" + idx++);
            });
        }

        var carousel = this;

        this.nextClick = function() {
            carousel.stopAuto();
            if (carousel.autoSOI) {
                carousel.disableAuto();
            }
            carousel.next();
        };

        this.prevClick = function() {
            carousel.stopAuto();
            if (carousel.autoSOI) {
                carousel.disableAuto();
            }
            carousel.prev();
        };

        jQuery(".jcarousel-next", this.c).click(this.nextClick);
        jQuery(".jcarousel-prev", this.c).click(this.prevClick);

        this.buttons(false, false);

        // Preload first items
        this.load(1, this.visible);
        this.scroll(1);
        this.startAuto();
    },

    get: function(idx)
    {
        return jQuery(".jcarousel-item-" + idx, this.list);
    },

    add: function(idx, s)
    {
        var item = this.get(idx);

        if (item.size() == 0) {
            var item = jQuery(document.createElement("li")).css({
                'float': 'left',
                'styleFloat': 'left',
                'width':  this.itemW + 'px',
                'height': this.itemH + 'px',
                'overflow': 'hidden',
                'list-style': 'none'
            }).addClass(".jcarousel-item-" + idx);

            jQuery(this.list).append(item);
            this.size++;
            this.resize();
        }

        jQuery(item).html(s);

        return item;
    },

    available: function(first, last)
    {
        if (this.end >= last) {
            return true;
        }

        this.end = last;
        return false;
    },

    resize: function()
    {
        if (this.size == 0) {
            return;
        }

        if (this.horiz) {
            var listW = this.size * this.itemD + 100;
            var listH = this.itemH;
        } else {
            var listW = this.itemW;
            var listH = this.size * this.itemD + 100;
        }

        jQuery(this.list).css({
            'width':  listW + 'px',
            'height': listH + 'px'
        });
    },

    scroll: function(idx)
    {
        idx = idx < 1 ? 1 : idx;

        var last = idx + this.visible - 1;
        last = (last > this.size) ? this.size : last;

        var first = last - this.visible + 1;
        first = (first < 1) ? 1 : first;

        this.first = first;
        this.last = first + this.visible - 1;

        // Preload next items
        this.load(this.last + 1, this.last + this.step);

        var pos = this.itemD * (this.first - 1) * -1;
        var arg = this.horiz ? {'left': pos} : {'top': pos};

        if (this.ani) {
            var carousel = this;
            jQuery(this.list).animate(arg, this.ani, function() {
               carousel.fixPosition();
            });
        } else {
            jQuery(this.list).css(this.horiz ? 'left' : 'top', pos + 'px');
        }
    },

    fixPosition: function()
    {
        if (this.first == 1) {
            // For some reason, jQuery.animate() don't wants to move the list
            // to the startposition...
            jQuery(this.list).css({
                'top': this.top,
                'left': this.left
            });
        }
    },

    buttons: function(next, prev)
    {
        var carousel = this;

        if (this.nextHandler != null) {
            var nextHandler = this.nextHandler;
            jQuery(".jcarousel-next", this.c).each(function() {
                nextHandler(carousel, this, next);
            });

            next ? jQuery(".jcarousel-next", this.c).click(this.nextClick) : jQuery(".jcarousel-next", this.c).unclick(this.nextClick);
        }

        if (this.prevHandler != null) {
            var prevHandler = this.prevHandler;
            jQuery(".jcarousel-prev", this.c).each(function() {
                prevHandler(carousel, this, prev);
            });

            prev ? jQuery(".jcarousel-prev", this.c).click(this.prevClick) : jQuery(".jcarousel-prev", this.c).unclick(this.prevClick);
        }
    },

    load: function(first, last)
    {
        if (this.loadHandler != null) {
            this.buttons(false, false);
            var available = this.available(first, last);
            // loaded() must be called by loadItemHandler()
            this.loadHandler(this, first, last, available);
        } else {
            this.loaded();
        }
    },

    loaded: function()
    {
        if (this.first > 1 && this.last < this.size) {
            this.buttons(true, true);
        } else if (this.first == 1 && this.last < this.size) {
            this.buttons(true, false);
        } else if (this.first > 1 && this.last >= this.size) {
            this.buttons(this.wrap, true);
        }
    },

    next: function()
    {
        this.scroll((this.wrap && this.last == this.size) ? 1 : this.first + this.step);

        if (this.wrap || this.last < this.size) {
            this.startAuto();
        }
    },

    prev: function()
    {
        this.scroll(this.first - this.step);
        this.startAuto();
    },

    startAuto: function()
    {
        if (this.autoD > 0) {
            var carousel = this;
            this.autoT = setTimeout(function() { carousel.next(); }, this.autoD * 1000);
        }
    },

    stopAuto: function()
    {
        if (this.autoT !== null) {
            clearTimeout(this.autoT);
            this.autoT = null;
        }
    },

    disableAuto: function()
    {
        this.stopAuto();
        this.autoD = 0;
    },

    intval: function(v)
    {
        v = parseInt(v);
        return isNaN(v) ? 0 : v;
    }
};

