(function($) {


	$.fn.simpleCarousel = function(options)
	{
		// if speed alias used instead of duration
		if (!options.duration && options.speed) options.duration = options.speed;
		
		// set options, overwriting defaults
		var o = $.extend({}, $.fn.simpleCarousel.defaults, options);

		return this.each(function()
		{
			var container = $(this);
			//var content = $('ul',this);

			var items = (o.slideExpr) ? $(this).find(o.slideExpr) : $(this).children();
			items.wrapAll('<div id="test"></div>');
			var content = $(this).children('div');
			var item_cnt = items.length;

			// circular
			if (o.circular && item_cnt > o.visible)
			{
				// create a unique id for each element
				var i = 0;
				items.each(function()
				{
					$(this).data('id',i++);
				});

				// clone the first set of visible elements (include the id)
				items.slice(0,o.visible).each(function()
				{
					$(this).clone().data('id',$(this).data('id')).appendTo(content);
				});

				// clone the last set of visible elements (include the id)
				// additionally reverse the array so the elements are placed properly
				var last_items = $.makeArray(items.slice(-(o.visible)));
				last_items.reverse();
				$(last_items).each(function()
				{
					$(this).clone().data('id',$(this).data('id')).prependTo(content);
				});

				// since elements were prepended, adjust the starting position
				o.start += o.visible;
			}

			// linear
			else
			{
				// prev
				if (o.start < o.visible) 
				{
					$(o.btnPrev).addClass('disabled');

					// atStart callback
					if (o.atStart) o.atStart.call(this,vis(o.start));
				}
				
				// next
				else if (o.start >= (item_cnt-o.visible)) 
				{
					$(o.btnNext).addClass('disabled');

					// atEnd callback
					if (o.atEnd) o.atEnd.call(this,vis(o.start));
				}
			}

			//items = $('li',this);
			item_cnt = items.length;

			var running = false;
			var animCss = o.vertical ? "top" : "left" ;
			var sizeCss = o.vertical ? "height" : "width" ;
			var item_current = o.start;
			var item_size = o.vertical ? items.outerHeight(true) : items.outerWidth(true) ;
			var content_size = item_size * item_cnt;
			var container_size = item_size * o.visible;

			//1
			items.css
			({
				'overflow': 'hidden',
				'float': o.vertical ? 'none' : 'left',
				'width': items.width(),
				'height': items.height()
			});

			//2
			content.css
			({
				'margin': 0, 
				'padding': 0
			})
			.css(sizeCss,content_size +'px');

			// set starting position
			if (o.start < o.visible || (!o.circular && o.start < o.visible)) item_current = o.start = 0;
			if (!o.circular && o.start >= (item_cnt - o.visible)) item_current = o.start = item_cnt - o.visible;
			content.css(animCss,-(item_current * item_size));

			//3
			container.css
			({
				'overflow': 'hidden', 
				'left': 0
			})
			.css(sizeCss,container_size +'px'); 

			// unhide hidden navigation
			if (item_cnt > o.visible)
			{
				$(o.btnPrev).removeClass('hidden');
				$(o.btnNext).removeClass('hidden');
			}

			if(o.btnPrev)
			{
				$(o.btnPrev).click(function(e) 
				{
					e.preventDefault();
					return go(item_current - o.scroll);
				});

				$(o.btnPrev).mouseenter(function(e)
				{
					e.preventDefault();
					$(this).addClass('over');
				});

				$(o.btnPrev).mouseleave(function(e)
				{
					e.preventDefault();
					$(this).removeClass('over');
				});
			}

			if(o.btnNext)
			{
				$(o.btnNext).click(function(e) 
				{
					e.preventDefault();
					return go(item_current + o.scroll);
				});

				$(o.btnNext).mouseenter(function(e)
				{
					e.preventDefault();
					$(this).addClass('over');
				});

				$(o.btnNext).mouseleave(function(e)
				{
					e.preventDefault();
					$(this).removeClass('over');
				});
			}

			function vis(from) 
			{
				from = from != undefined ? from : item_current ;
				return items.slice(from,from+o.visible);
			}

			function go(to) 
			{
				if(!running) 
				{
					running = true;

					var i;
					var go_to;

					// circular
					if (o.circular)
					{
						// prev
						if (to < 0)
						{
							for (i=item_current+1; i<item_cnt; i++)
							{
								if ($(items[i]).data('id') == $(items[item_current]).data('id'))
								{
									content.css(animCss,-(i*item_size));
									go_to = i-(Math.abs(to)+item_current);
									break;
								}
							}
						}

						// next
						else if (to > (item_cnt - o.visible))
						{
							for (i=0; i<item_cnt; i++)
							{
								if ($(items[i]).data('id') == $(items[item_current]).data('id'))
								{
									content.css(animCss,-(i*item_size));
									go_to = i+(to-item_current);
									break;
								}
							}
						}

						// between
						else  go_to = to;
					}

					// linear
					else
					{
						// prev
						if (to <= 0) 
						{
							go_to = 0;
							$(o.btnPrev).addClass('disabled');
							$(o.btnNext).removeClass('disabled');

							// atStart callback
							if (o.atStart) o.atStart.call(this,vis(go_to));
						}
						
						// next
						else if (to >= (item_cnt-o.visible)) 
						{
							go_to = item_cnt - o.visible;
							$(o.btnPrev).removeClass('disabled');
							$(o.btnNext).addClass('disabled');

							// atEnd callback
							if (o.atEnd) o.atEnd.call(this,vis(go_to));
						}
						
						// between
						else 
						{
							go_to = to;
							$(o.btnPrev).removeClass('disabled');
							$(o.btnNext).removeClass('disabled');

							// inBetween callback
							if (o.inBetween) o.inBetween.call(this,vis(go_to));
						}
					}

					if (go_to == item_current) running = false;
					else
					{
						// beforeStart callback
						if(o.beforeStart) o.beforeStart.call(this,vis());

						// set current item
						item_current = go_to;

						var params = {};
						params[animCss] = -(go_to*item_size);

						content.animate
						(
							params
							, o.duration ? o.duration : o.speed
							, o.easing
							, function()
							{
								// afterEnd callback
								if (o.afterEnd) o.afterEnd.call(this,vis());
								running = false;
							}
						);
					}
				}

				return false;
			}

			$(this).bind('go',function(e)
			{
				go(e.idx);
			});
		});
	}

	$.fn.simpleCarousel.defaults = 
	{
		slideExpr: null
		, btnPrev : null
		, btnNext : null
		//, btnGo: null // not implemented
		, duration: 500
		, easing: null
		, circular: false
		, visible: 3
		, scroll: 1
		, start: 0
		//, auto: 500 // not implemented
		//, mouseWheel: 0 // not implemented
		, vertical: false
		, beforeStart: null // callback
		, afterEnd: null // callback
		, atStart: null //callback
		, atEnd: null // callback
		, inBetween: null //callback
	};

})(jQuery);
