;(function (factory)
{
    if (typeof define === 'function' && define.amd)
    {
        define(['jquery'], factory);
    }
    else if (typeof exports === 'object')
    {
        factory(require('jquery'));
    }
    else
    {
        factory(jQuery);
    }
}
(function ($)
{
    var pluginName = "tinycarousel"
    ,   defaults   =
        {
            start:          0      // The starting slide
        ,   axis:           "x"    // vertical or horizontal scroller? ( x || y ).
        ,   buttons:        false   // show left and right navigation buttons.
        ,   bullets:        false  // is there a page number navigation present?
        ,   interval:       true  // move to another block on intervals.
        ,   intervalTime:   3000   // interval time in milliseconds.
        ,   animation:      true   // false is instant, true is animate.
        ,   animationTime:  1000   // how fast must the animation move in ms?
        ,   infinite:       true   // infinite carousel.
        }
    ;

    function Plugin($container, options)
    {
        this.options   = $.extend({}, defaults, options);
        this._defaults = defaults;
        this._name     = pluginName;

        var self      = this
        ,   $viewport = $container.find(".viewport:first")
        ,   $overview = $container.find(".overview:first")
        ,   $slides   = 0
        ,   $next     = $container.find(".next:first")
        ,   $prev     = $container.find(".prev:first")
        ,   $bullets  = $container.find(".bullet")

        ,   viewportSize    = 0
        ,   contentStyle    = {}
        ,   slidesVisible   = 0
        ,   slideSize       = 0
        ,   slideIndex      = 0

        ,   isHorizontal  = this.options.axis === 'x'
        ,   sizeLabel     = isHorizontal ? "Width" : "Height"
        ,   posiLabel     = isHorizontal ? "left" : "top"
        ,   intervalTimer = null
        ;

        this.slideCurrent = 0;
        this.slidesTotal  = 0;

        function initialize()
        {
            self.update();
            self.move(self.slideCurrent);

            setEvents();

            return self;
        }

        this.update = function()
        {
            $overview.find(".mirrored").remove();

            $slides           = $overview.children();
            viewportSize      = $viewport[0]["offset" + sizeLabel];
            slideSize         = $slides.first()["outer" + sizeLabel](true);
            self.slidesTotal  = $slides.length;
            self.slideCurrent = self.options.start || 0;
            slidesVisible     = Math.ceil(viewportSize / slideSize);

            $overview.append($slides.slice(0, slidesVisible).clone().addClass("mirrored"));
            $overview.css(sizeLabel.toLowerCase(), slideSize * (self.slidesTotal + slidesVisible));

            return self;
        };

        function setEvents()
        {
            if(self.options.buttons)
            {
                $prev.click(function()
                {
                    self.move(--slideIndex);

                    return false;
                });

                $next.click(function()
                {
                    self.move(++slideIndex);

                    return false;
                });
            }

            $(window).resize(self.update);

            if(self.options.bullets)
            {
                $container.on("click", ".bullet", function()
                {
                    self.move(slideIndex = +$(this).attr("data-slide"));

                    return false;
                });
            }
        }

        this.start = function()
        {
            if(self.options.interval)
            {
                clearTimeout(intervalTimer);

                intervalTimer = setTimeout(function()
                {
                    self.move(++slideIndex);

                }, self.options.intervalTime);
            }

            return self;
        };

        this.stop = function()
        {
            clearTimeout(intervalTimer);

            return self;
        };

        this.move = function(index)
        {
            slideIndex        = index;
            self.slideCurrent = slideIndex % self.slidesTotal;

            if(slideIndex < 0)
            {
                self.slideCurrent = slideIndex = self.slidesTotal - 1;
                $overview.css(posiLabel, -(self.slidesTotal) * slideSize);
            }

            if(slideIndex > self.slidesTotal)
            {
                self.slideCurrent = slideIndex = 1;
                $overview.css(posiLabel, 0);
            }

            contentStyle[posiLabel] = -slideIndex * slideSize;

            $overview.animate(
                contentStyle
            ,   {
                    queue    : false
                ,   duration : self.options.animation ? self.options.animationTime : 0
                ,   always : function()
                    {
                        $container.trigger("move", [$slides[self.slideCurrent], self.slideCurrent]);
                    }
            });

            setButtons();
            self.start();

            return self;
        };

        function setButtons()
        {
            if(self.options.buttons && !self.options.infinite)
            {
                $prev.toggleClass("disable", self.slideCurrent <= 0);
                $next.toggleClass("disable", self.slideCurrent >= self.slidesTotal - slidesVisible);
            }

            if(self.options.bullets)
            {
                $bullets.removeClass("active");
                $($bullets[self.slideCurrent]).addClass("active");
            }
        }

        return initialize();
    }

    $.fn[pluginName] = function(options)
    {
        return this.each(function()
        {
            if(!$.data(this, "plugin_" + pluginName))
            {
                $.data(this, "plugin_" + pluginName, new Plugin($(this), options));
            }
        });
    };
}));