(function ( $ )
{
	$.fn.jMyCarousel = function(o)
	{   
		o = $.extend({
			btnPrev: null,
			btnNext: null,
			mouseWheel: true,
			auto: false,

			speed: 500,
			easing: 'linear',

			vertical: false,
			circular: true,
			visible: '4',
			start: 0,
			scroll: 1,

			step: 50,
			eltByElt: false,
			evtStart: 'mouseover',
			evtStop: 'mouseout',
			beforeStart: null,
			afterEnd: null,
		}, o || {});

		return this.each(function()
		{
			window.jCl = {	forward: function(){forward()},
								backward: function(){backward()},
								toBegining: function(){toStart()},
								toEnd: function(){toFinish()},
							};
			
			var running = false;
			var animCss = o.vertical ? 'top' : 'left';
			var sizeCss = o.vertical ? 'height' : 'width';
			var div = $(this);
			var ul = $('ul', div);
			var tLi = $('li', ul);
			var tl = tLi.size();
			var v = o.visible;
			var mousewheelN = 0;
			var defaultBtn = ( o.btnNext === null && o.btnPrev === null );
			var cssU = ( v.toString().indexOf('%') != -1 ? '%' : (v.toString().indexOf('px') != -1) ? 'px' : 'el');
			var direction = null;
			
			var begining = 0;
			var end = 0;
			
			
			if(o.circular)
			{
				var imgSet = tLi.clone();
				ul.prepend(imgSet).append(imgSet.clone());
			}
						 
			var li = $('li', ul);
			div.css('visibility', 'visible');
			
			li.css('overflow', 'hidden')
				.css('float', o.vertical ? 'none' : 'left')
				.children().css('overflow', 'hidden');

			if(!o.vertical) li.css('display', 'inline');
			
			if(li.children().get(0).tagName.toLowerCase() == 'a' && !o.vertical)
				li.children().css('float','left');

			if(o.vertical && jQuery.browser.msie)
				li.css('line-height', '4px').children().css('margin-bottom', '-4px');

			ul.css('margin', '0')
				.css('padding', '0')
				.css('position', 'relative')
				.css('list-style-type', 'none')
				.css('z-index', '1');

			div.css('overflow', 'hidden')
				.css('position', 'relative')
				.css('z-index', '2')
				.css('left', '0px');
		  
			var liSize = (o.vertical) ? height(li) : width(li);
			var liSizeV = (o.vertical) ? elHeight(li) : height(li);
			var curr = o.start;
			var nbAllElts = li.size();
			var ulSize = get_fullsize(li);
			var nbElts = tl;
			var eltsSize = ulSize;
			var allEltsSize = ulSize;
			
			var step = (o.step == 'default') ? liSize : o.step;
			
			var divSize, cssSize, cssUnity;

			if(cssU == '%')
			{
				divSize = 0;
				cssSize = parseInt(v);  
				cssUnity = "%";
			}
			else if(cssU == 'px')
			{
				divSize = parseInt(v);
				cssSize = parseInt(v);
				cssUnity = "px";
			}
			else
			{
				divSize = liSize * parseInt(v); 
				cssSize = liSize * parseInt(v);
				cssUnity = "px";
			}  

			if( divSize > ulSize ) return false;
		  

			o.btnPrev = defaultBtn ? $('<input type="button" class="' + (o.vertical ? 'up' : 'prev') + '" />') : $(o.btnPrev);
			o.btnNext = defaultBtn ? $('<input type="button" class="' + (o.vertical ? 'down' : 'next') + '" />') : $(o.btnNext);
			var prev = o.btnPrev;
			var next = o.btnNext;
		  
			if(defaultBtn && o.auto !== true)
			{
				prev.css({'opacity': '0.6'});
				next.css({'opacity': '0.6'});
				div.prepend(prev);
				div.prepend(next);
				o.btnPrev = prev;
				o.btnNext = next;
			}

			if(o.eltByElt)
			{ 
				step = liSize;
				if(o.start % liSize !== 0)
				{
					var imgStart = parseInt(o.start / liSize);
					curr = o.start = (imgStart * liSize);
				}
			}

			if(o.circular)
			{
				o.start += (liSize * tl);
				curr += (liSize * tl);
			}
			

			ul.css(sizeCss, ulSize + "px")
				.css(animCss, -(o.start));
			div.css(sizeCss, cssSize + cssUnity);
			
			if(o.vertical && cssUnity == '%')
			{
				var pxsize = ((liSize * nbElts) * (parseInt(v) / 100));
				div.css(sizeCss,  pxsize + 'px');
			}
		  
			if(divSize === 0) divSize = div.width();

			if(o.vertical)
			{
				div.css("width" , liSizeV + 'px');
				ul.css("width", liSizeV + 'px');
				li.css('margin-bottom', (parseInt(li.css('margin-bottom')) * 2) + 'px');
				li.eq(li.size() - 1).css('margin-bottom', li.css('margin-top'));
			}
			else
			{
				div.css('height', liSizeV + 'px');
				ul.css('height', liSizeV + 'px');	
			}
								

			if(cssU == '%')
			{
				v = divSize / li.width();						
				if(v % 1 !== 0) v++;
				v = parseInt(v);
			}

			var divVSize = div.height();

			if(defaultBtn)
			{
				next.css({'z-index':200, 'position':'absolute'});
				prev.css({'z-index':200, 'position':'absolute'});

				if(o.vertical)
				{
					prev.css(
					{
						'width': prev.width(),
						'height': prev.height(),
						'top': '0px',
						'left': parseInt(liSizeV / 2) - parseInt(prev.width() / 2) + 'px'
					});
					
					next.css(
					{
						'width': prev.width(),
						'height': prev.height(),
						'top': (divVSize - prev.height()) + 'px',
						'left': parseInt(liSizeV / 2) - parseInt(prev.width() / 2) + 'px'
					});
				}
				else
				{
					prev.css({'left': '0px', 'top': parseInt(liSizeV / 2) - parseInt(prev.height() / 2) + 'px'});
					next.css({'right': '0px', 'top': parseInt(liSizeV / 2) - parseInt(prev.height() / 2) + 'px'});
				}
			}

			if(o.btnPrev)
			{
				$(o.btnPrev).bind(o.evtStart, function()
				{
					if(defaultBtn) o.btnPrev.css('opacity', 0.9);
					running = true;
					direction = 'backward';
					return backward(); 
				});

				$(o.btnPrev).bind(o.evtStop, function()
				{
					if(defaultBtn){ o.btnPrev.css('opacity', 0.6); }
					running = false; 
					direction = null;
					return stop(); 
				});
			}

			if(o.btnNext)
			{
				$(o.btnNext).bind(o.evtStart, function()
				{
					if(defaultBtn) o.btnNext.css('opacity', 0.9);
					running = true;
					direction = 'forward';
					return forward(); 
				});
				
				$(o.btnNext).bind(o.evtStop,function()
				{
					if(defaultBtn) o.btnNext.css('opacity', 0.6);
					running = false;
					direction = null;
					return stop(); 
				});
			}

			if(o.auto)
			{
				running = true;	
				forward();	
			}		
			
			if(o.mouseWheel && div.mousewheel)
			{
				div.mousewheel(function(e, d)
				{ 
					if(!o.circular
						&& (d > 0 ? (curr + divSize < ulSize) : (curr > 0))
						|| O.circular)
					{
						mousewheelN++;
						
						if(!running)
						{
							if(d > 0) forward(step, true);
							else
							{
								backward(step, true);
						
								running = true;
							}
						}
					}
				});
			}

			function get_fullsize(e)
			{
				var fsize = 0;
				
				for(var xc=0; xc<e.length; xc++)
				{
					if(o.vertical) fsize += e[xc].offsetHeight;
					else fsize += e[xc].offsetWidth;
				}
				
				return fsize;
			}

			function forward(stepsize, once)
			{
				var s = stepsize ? stepsize : step;

				if(running && direction == 'backward')  return;
				
				if(!o.circular)
				{	
					if(curr + s + (o.vertical ? divVSize : divSize) > eltsSize)
						s = eltsSize - (curr + (o.vertical ? divVSize : divSize));
				}
				
				ul.animate(
					(animCss == 'left') ? { left: -(curr + s) } : { top: -(curr + s) },
					o.speed,
					o.easing,
					function()
					{
						curr += s;
						
						if(o.circular)
						{
							if(curr + (o.vertical ? divVSize : divSize) + liSize >= allEltsSize)
							{
								ul.css(o.vertical ? 'top' : 'left', -curr + eltsSize);
								curr -= eltsSize;
							}
						}
					
						if(!once && running) forward();
						else if(once)
						{
							if(--mousewheelN > 0) this.forward(step, true);
							else
							{
								running = false;
								direction = null;
							}
						}
					}
				);
			}
			
			function backward(stepsize, once)
			{
				var s = stepsize ? stepsize : step;
				
				if(running && direction == 'forward') return;
				
				if(!o.circular)
					if(curr - s  < 0) s = curr - 0;
				
				ul.animate(
					(animCss == 'left') ? { left: -(curr - s) } : { top: -(curr - s) },
					o.speed,
					o.easing,
					function()
					{
						curr -= s;
						
						if(o.circular)
						{
							if(curr <= liSize)
							{
								ul.css(o.vertical ? 'top' : 'left', -(curr + eltsSize));
								curr += eltsSize;
							}
						}
						  
						if(!once  && running) backward();
						else if(once)
						{
							if(--mousewheelN > 0) backward(step, true);
							else{
								running = false;
								direction = null;
							}
						}
					}
				);
			}
			
			function toStart()
			{
				curr = 0;
				backward();
			}
			
			function toFinish()
			{
				curr = eltsSize;
				forward();
			}
		
			function stop()
			{
				if(!o.eltByElt)
				{
					ul.stop();
					curr = 0 - parseInt(ul.css(animCss));
				}
				
				running = false;
				direction = null;
			}

			function imgSize(el, dimension)
			{
				if(dimension == 'width') return el.find('img').width();
				else return el.find('img').height();
			}

			function elHeight(el)
			{
				var elImg = el.find('img');
				if(o.vertical)
					return parseInt(el.css('margin-left')) + parseInt(el.css('margin-right')) + parseInt(elImg.width()) + parseInt(el.css('border-left-width')) + parseInt(el.css('border-right-width')) + parseInt(el.css('padding-right')) + parseInt(el.css('padding-left'));
				else
					return parseInt(el.css('margin-top')) + parseInt(el.css('margin-bottom')) + parseInt(elImg.width()) + parseInt(el.css('border-top-height')) + parseInt(el.css('border-bottom-height')) + parseInt(el.css('padding-top')) + parseInt(el.css('padding-bottom'));
			}
			  
			function debug(html)
			{
				$('#debug').html($('#debug').html() + html + '<br/>');
			}
		});
	};

	function css(el, prop)
	{
		return parseInt($.css(el[0], prop)) || 0;
	}

	function width(el)
	{
		return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
	}

	function height(el)
	{
		return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
	}

})(jQuery);
