(function($)
{

window.Carousel = function(element, options)
{
	if (!options) { options = { idx:0 }; }
	else if (!options.idx) { options.idx = 0; }
	
	this.carousel = element;
	
	this.carouselList = $('> .carousel-items:first', this.carousel);
	this.carouselItems = $('> .carousel-item', this.carouselList);
	this.itemCount = this.carouselItems.length;
	this.lastIdx = this.itemCount - 1;
	
	if (options.idx < 0) { options.idx = 0; }
	else if (options.idx > this.lastIdx) { options.idx = this.lastIdx; }
	
	this.itemWidth = parseInt($(this.carouselItems.get(0)).css('width'));
	this.viewportWidth = Math.round(this.itemWidth * this.itemCount);
	this.offset = Math.round(this.viewportWidth - this.itemWidth) / 2;
	
	this.carouselList.css('width', this.viewportWidth + 'px');
	
	this.firstIdx = 0;
	this.currentIdx = options.idx;
	this.currentOffset = parseInt(this.carouselList.css('marginLeft'));
	this.isAnimating = false;
	
	this._bindEventListeners();
	
	Carousel._instances['Carousel_' + (++Carousel._instanceCounter)] = this;
	
	return this;
}
Carousel.prototype =
{
	next: function()
	{
		if (this.isAnimating || this.currentIdx >= this.lastIdx) { return; }
		
		var offset = this._calculateIndexOffset(this.currentIdx + 1);
		if (!offset) { return; }
		
		this.currentIdx = offset.idx;
		this._move(offset.offset);
	},
	prev: function()
	{
		if (this.isAnimating || this.currentIdx <= this.firstIdx) { return; }
		
		var offset = this._calculateIndexOffset(this.currentIdx - 1);
		if (!offset) { return; }
		
		this.currentIdx = offset.idx;
		this._move(offset.offset);
	},
	jumpTo: function(idx)
	{
		if (this.isAnimating || idx < this.firstIdx || idx > this.lastIdx) { return; }
		
		var offset = this._calculateIndexOffset(idx);
		if (!offset) { return; }
		
		this.currentIdx = offset.idx;
		this._move(offset.offset, true);
	},
	_calculateIndexOffset: function(idx)
	{
		if (idx < 0 || idx > this.lastIdx || idx == this.currentIdx) { return null; }
		
		var currentIdx = this.currentIdx;
		var currentOffset = this.currentOffset;
		if (idx > currentIdx)
		{
			while (currentIdx < idx)
			{
				currentIdx++;
				currentOffset -= this.itemWidth;
			}
		}
		else
		{
			while (currentIdx > idx)
			{
				currentIdx--;
				currentOffset += this.itemWidth;
			}
		}
		return { offset:currentOffset, idx:currentIdx };
	},
	_move: function(offset, bypassAnimation)
	{
		if (typeof(offset) !== 'number') { return; }
		
		var prevBtn = $('a.prev', this.carousel);
		var nextBtn = $('a.next', this.carousel);
	
		this.currentOffset = offset;
		
		if (bypassAnimation)
		{
			this.carouselList.css('marginLeft', offset + 'px');
		}
		else
		{
			var scope = this;
			this.isAnimating = true;
			this.carouselList.animate({ marginLeft: offset + 'px' }, 200, 'swing', function(evt){ scope.isAnimating = false; });
		}
			
		// Determine button availability
		if (this.currentIdx < this.lastIdx)
		{
			nextBtn.removeClass('disabled');
		}
		if (this.currentIdx > this.firstIdx)
		{
			prevBtn.removeClass('disabled');
		}

		if (this.currentIdx === this.lastIdx)
		{
			nextBtn.addClass('disabled');
		}
		else if (this.currentIdx === this.firstIdx)
		{
			prevBtn.addClass('disabled');
		}
	},
	
	_bindEventListeners: function()
	{
		var scope = this;
		var nextBtn = $('> .next', this.carousel);
		var prevBtn = $('> .prev', this.carousel);
		
		nextBtn.click(function(evt)
		{
			evt.preventDefault();
			if ($(this).hasClass('disabled')) { return; }
			scope.next();
		});
		
		prevBtn.click(function(evt)
		{
			evt.preventDefault();
			if ($(this).hasClass('disabled')) { return; }
			scope.prev();
		});
		
		$('.prev,.next', this.carousel).focus(function(evt)
		{
			evt.preventDefault(); this.blur();
		});
		
		if (this.currentIdx === this.firstIdx)
		{
			prevBtn.addClass('disabled');
		}
		else if (this.currentIdx === this.lastIdx)
		{
			nextBtn.addClass('disabled');
		}
	}
};
Carousel._instanceCounter = 0;
Carousel._instances = {},
Carousel.get = function(element)
{
	var carousel = null;
	for (var id in Carousel._instances)
	{
		if (Carousel._instances[id].carousel === element)
		{
			carousel = Carousel._instances[id];
			break;
		}
	}
	return carousel;
};

})(jQuery);