(function($) {
    $.widget("ui.novucarousel", {
        _init: function() {
            this.options.animationspeed *= 1000;
            this.options.framespeed *= 1000;

            if (this.element[0].nodeName == 'UL' || this.element[0].nodeName == 'OL') {
                this.list = this.element;

                this.listItems = this.list.children('li');
                this.listItems.css({ float: 'left', listStyle: 'none' });

                this.list.css({ padding: 0, overflow: 'hidden', marginTop: 0, marginBottom: 0 });

                /* add the clipper div around the items */
                this.clipper = $('<div class="carousel-clip"></div>').css('overflow', 'hidden');
                this.list.wrap(this.clipper);
                this.clipper = this.list.parent();

                this._updateSizes();

                this.pausing = false;

                if (this.options.auto)
                    this.start();

                this._setOuterWidth();

                this.options.framespeed = Math.max(this.options.animationspeed, this.options.framespeed);

                var self = this;
                if (!this.options.playOnHover) {
                    if (this.options.buttonNext) {
                        $(this.options.buttonNext).click(function() {
                            self.next();
                        });
                    }

                    if (this.options.buttonPrev) {
                        $(this.options.buttonPrev).click(function() {
                            self.prev();
                        });
                    }
                }
            }
        },

        /* necessary for edit mode in webparts */
        _setOuterWidth: function() {
            var par = this.clipper.parent();
            this.clipper.css('display', 'none');
            par.css('width', par.width());
            this.clipper.css('display', 'block');
        },

        _updateSizes: function() {
            var maxHeight = 0;
            this.totalWidth = 0;

            this.screens = [];

            var self = this;

            this.listItems = this.list.children('li'); //update listitems, possible new ones
            this.listItems.each(function(i) {
                var el = $(this);
                maxHeight = Math.max(maxHeight, el.outerHeight());
                self.totalWidth += el.outerWidth(true);

                self.screens[i] = {
                    width: el.outerWidth(),
                    height: el.outerHeight(),
                    element: el
                }
            });

            this.list.css('width', this.totalWidth);
            this.clipper.css('height', maxHeight + 'px');

            this.clipperWidth = this.clipper.innerWidth();

            this.current = 0;
        },

        pause: function() {
            //console.log('pause');
            this.pausing = true;
        },

        unpause: function() {
            //console.log('unpause');
            this.pausing = false;
        },

        start: function() {
            this._stopTimer();
            var self = this;
            this.playing = true;
            setTimeout(function() { self._animate() }, this.options.framespeed);
        },

        stop: function() {
            this.playing = false;
            this._stopTimer();
        },

        _stopTimer: function() {
            if (this.timer)
                clearTimeout(this.timer);
        },

        _animate: function() {
            this._stopTimer();

            if (!this.clipper.hasMouse()) {

                if (this.playing && !this.pausing && !this.options.playOnHover)
                    this.next();

                if (this.playing && !this.pausing && this.options.playOnHover) {
                    if (this.options.buttonNext && $(this.options.buttonNext).hasMouse()) {
                        this.next();
                    } else if (this.options.buttonPrev && $(this.options.buttonPrev).hasMouse()) {
                        this.prev();
                    }
                }

            }

            var self = this;
            this.timer = setTimeout(function() { self._animate() }, this.options.framespeed);
        },

        next: function() {
            if (this.totalWidth < this.clipperWidth)
                return;

            this.list.stop(true, true); //stop all animations
            if (this.options.circular)
                this._circularNext();
            else
                this._nonCircularNext();
        },

        _nonCircularNext: function() {
            var pos = this._getScreenPosition(this.current + 1);
            var maxpos = this.totalWidth - this.clipperWidth;

            if (pos < maxpos && this.current < this.listItems.size()) {
                this.current++;
                this._scrollTo(-pos);
            } else {
                if (this._scrollTo(-maxpos)) {
                    this.current++;
                }
            }
        },

        _circularNext: function() {
            //clone the first item and place it in the back, and update the situation
            var firstItem = $(this.listItems[0]);
            var clone = firstItem.clone(true).prependTo(this.list);
            firstItem.appendTo(this.list);
            this._updateSizes();

            var pos = this._getScreenPosition(1); //get second frame position
            var self = this;

            //scrollTo second frame and remove first frame
            this._scrollTo(-pos, function() {
                clone.remove();
                self.list.css('margin-left', 0);
                self._updateSizes();
            });
        },

        prev: function() {
            if (this.totalWidth < this.clipperWidth)
                return;

            this.list.stop(true, true); //stop all animations
            if (this.options.circular)
                this._circularPrev();
            else
                this._nonCircularPrev();
        },

        _nonCircularPrev: function() {
            if (this.current > 0)
                this.current--;

            var pos = this._getScreenPosition(this.current);
            this._scrollTo(-pos);
        },

        _circularPrev: function() {
            var lastItem = $(this.listItems[this.listItems.length - 1]);
            var clone = lastItem.clone(true).appendTo(this.list);
            lastItem.prependTo(this.list);

            this._updateSizes();

            this.list.css('margin-left', -this.screens[0].width);

            var self = this;
            this._scrollTo(0, function() {
                clone.remove();
                self._updateSizes();
            });
        },

        _getScreenPosition: function(idx) {
            var sum = 0;

            for (var i = 0; i < idx; ++i)
                sum += this.screens[i].width;

            return sum;
        },

        //returns true if there was a movement
        _scrollTo: function(pos, f) {
            if (parseInt(this.list.css('margin-left')) != pos) {
                this.list.animate({ marginLeft: pos }, this.options.animationspeed, this.options.animationType, f);
                return true;
            }
            if (f)
                f();
            return false;
        },

        destroy: function() {
            $.widget.prototype.apply(this, arguments);
        }
    });

    $.extend($.ui.novucarousel, {
        getter: "",
        defaults: {
            circular: true,
            auto: true,
            playOnHover: false,
            animationspeed: 0.25,
            framespeed: 1.0,
            animationType: 'swing',
            buttonNext: null,
            buttonPrev: null
        }
    });

})(jQuery);