
var MyCarousel = Class.create();
Object.extend(
	MyCarousel.prototype, {

	    initialize: function(obj, items, numDisplay, stepDelay, stepSize, direction) {

	        // set instance fields from constructor params
	        this._obj = $(obj);
	        this._items = items;
	        this._numDisplay = numDisplay;
	        this._stepDelay = stepDelay;
	        this._stepSize = stepSize;
	        this._direction = direction;
	        this._kill = false;

	        // set other instance fields
	        this._itemIndex = 0;
	        this._itemsArrayLength = this._items.length;
	        this._viewportArrayLength = 2 * this._stepSize + this._numDisplay;
	        this._viewportItems = new Array(this._viewportArrayLength);
	        this._locked = false;
	        this._loop = true;

	        // set dom related fields
	        this._viewport = this._obj.getElementsByClassName('carouselViewport')[0];
	        this._carouselSlot1 = this._obj.getElementsByClassName('carouselSlot')[0];
	        this._carouselSlot2 = this._obj.getElementsByClassName('carouselSlot')[1];

	        this._prevButton = this._obj.getElementsByClassName('carouselPrevButton')[0];
	        this._nextButton = this._obj.getElementsByClassName('carouselNextButton')[0];

	        this._eL1 = this.previous.bindAsEventListener(this);
	        this._eL2 = this.next.bindAsEventListener(this);

	        Event.observe(this._prevButton, 'click', this._eL1);
	        Event.observe(this._nextButton, 'click', this._eL2);

	        // insert items in both slots
	        var viewportIndex = 0;
	        for (var i = 0; i < this._viewportArrayLength; i++) {
	            viewportIndex = i - this._stepSize;
	            new Insertion.Bottom(this._carouselSlot1, this._items[this.viewport2item(viewportIndex)]);
	            new Insertion.Bottom(this._carouselSlot2, this._items[this.viewport2item(viewportIndex)]);
	        }

	        // adjust style according to first items dimensions
	        var tmpItem = this._viewport.getElementsByClassName('item')[0];
	        this._viewport.setStyle({
	            width: (this._numDisplay * tmpItem.getWidth()) + 'px',
	            height: (tmpItem.getHeight()) + 'px'
	        });
	        this._carouselSlot2.setStyle({
	            'visibility': 'visible',
	            width: (this._viewportArrayLength * tmpItem.getWidth()) + 'px',
	            height: (tmpItem.getHeight()) + 'px',
	            left: (-this._stepSize * tmpItem.getWidth()) + 'px'
	        });
	        this._carouselSlot1.setStyle({
	            'visibility': 'hidden',
	            width: (this._viewportArrayLength * tmpItem.getWidth()) + 'px',
	            height: (tmpItem.getHeight()) + 'px',
	            left: (-this._stepSize * tmpItem.getWidth()) + 'px'
	        });

	        // set timed call to step() function (keeping this context)
	        var _this = this;
	        setTimeout(function() { _this.step(true) }, this._stepDelay);
	    },

	    viewport2item: function(viewportIndex) {
	        return (this._itemIndex + viewportIndex + this._itemsArrayLength) % (this._itemsArrayLength);
	    },

	    buildViewport: function() {
	        var viewportIndex = 0;

	        //repopulate hidden slot
	        this._carouselSlot1.innerHTML = '';
	        for (var i = 0; i < this._viewportArrayLength; i++) {
	            viewportIndex = i - this._stepSize;
	            new Insertion.Bottom(this._carouselSlot1, this._items[this.viewport2item(viewportIndex)]);

	        }

	        // adjust slots styles
	        var tmpItem = this._viewport.getElementsByClassName('item')[0];
	        this._carouselSlot1.setStyle({
	            'visibility': 'visible',
	            left: (-this._stepSize * tmpItem.getWidth()) + 'px'
	        });
	        this._carouselSlot2.setStyle({
	            'visibility': 'hidden',
	            left: (-this._stepSize * tmpItem.getWidth()) + 'px'
	        });

	        // switch slotreferences
	        var tmpSlot = this._carouselSlot1;
	        this._carouselSlot1 = this._carouselSlot2;
	        this._carouselSlot2 = tmpSlot;
	    },

	    step: function(loop) {
	        if (this._loop && this._kill == false) {
	            // synchronize...
	            this._locked = true;

	            // rebuild slots
	            this.buildViewport();

	            // update item index
	            this._itemIndex = ((-this._direction * this._stepSize) + this._itemIndex + this._itemsArrayLength) % (this._itemsArrayLength);

	            // calc step width for move effect				
	            var tmpItem = this._viewport.getElementsByClassName('item')[0];
	            var stepWidth = this._direction * this._stepSize * tmpItem.getWidth();

	            new Effect.Move(
					this._carouselSlot2,
					{
					    queue: {
					        position: 'end', scope: 'q' + this._obj.id
					    },
					    x: stepWidth,
					    y: 0,
					    mode: 'relative',
					    duration: 1.5
					}
				);


	            if (loop) {
	                // set timed call to step() function (keeping this context)
	                var _this = this;
	                setTimeout(function() { _this.step(true) }, this._stepDelay);
	            }
	        }
	        this._locked = false;
	    },

	    quickStep: function(direction) {
	        if (!this._locked) {
	            // synchronize...
	            this._locked = true;

	            // rebuild slots
	            this.buildViewport();

	            // stop looping of step() function
	            this._loop = false;

	            // clear and reinvoke timed restart of step() loop
	            clearTimeout(this._toEvent);
	            var _this = this;
	            this._toEvent = setTimeout(function() { _this._loop = true; _this.step(true) }, 15000);

	            // calc step width for move effect
	            var tmpItem = this._viewport.getElementsByClassName('item')[0];
	            var stepWidth = direction * this._stepSize * tmpItem.getWidth();

	            // update item index
	            this._itemIndex = ((-direction * this._stepSize) + this._itemIndex + this._items.length) % (this._items.length);

	            new Effect.Move(
					this._carouselSlot2, {
					    queue: {
					        position: 'end',
					        scope: this._obj.id
					    },
					    x: stepWidth,
					    y: 0,
					    mode: 'relative',
					    duration: 0.5,
					    afterFinish: function() {
					        _this._locked = false
					    }
					}
				);


	        }
	    },
	    next: function() {
	        this.quickStep(-1);
	    },
	    previous: function() {
	        this.quickStep(1);
	    },
	    kill: function() {
	        this._kill = true;
	    }
	}
);