(function($) {

$.fn.carousel = function(params)
{
	if (!params) params = {};
	var options = {};
	if (params.constructor == String) params = { action: params };
	$.extend(options, $.fn.carousel.defaults, params);

	return this.each(function() {
		switch (options.action)
		{
			case "stop":
				window.clearInterval($(this).data('timer'));
				return;
			case "pause":
				paused = true;
				return;
			case "resume":
				paused = false;
				return;
		}

		var angle = 0;
		var target = 0;
		var lastAngle = null;
		var paused = false;
		var parent = $(this);
		var kids = parent.children();
		var TWO_PI = Math.PI * 2.0;
		var speed = TWO_PI * -options.rotationsPerSecond / options.frameRate;
		var interval;

		if (options.ignore)
		{
			kids = kids.not(options.ignore);
		}
		if (options.leftButton)
		{
			kids = kids.not(options.leftButton);
			$(options.leftButton).click(function() {
				target = Math.round(angle / interval);
				target = (target + 1) * interval;
			});
		}
		if (options.rightButton)
		{
			kids = kids.not(options.rightButton);
			$(options.rightButton).click(function() {
				target = Math.round(angle / interval);
				target = (target - 1) * interval;
			});
		}

		interval = TWO_PI / kids.length;

		parent.css({ position: 'relative' });
		kids.css({ position: 'absolute' });
		parent.mouseenter(function() { paused = true; });
		parent.mouseleave(function() { paused = false; });

		function positionItem(i) {
			var myAngle = (i * interval) + angle;
			var sin = Math.sin(myAngle);
			var cos = Math.cos(myAngle);
			var $this = $(this);
			$this.css({
				left: ((sin * 0.5 + 1.0) * parent.width() - $this.width()) / 2,
				top: ((cos * 0.5 + 1.0) * parent.height() - $this.height())/ 2,
				// Chrome, at least, appears to mess up if the z-index isn't an int.
				// Round it down to avoid problems.
				'z-index': Math.floor(cos * options.zRadius + options.zCenter),
				opacity: Math.max(cos + 1.05, 0.5) / 2 - 0.1,
				width: Math.max(cos * 120, 50)
			});
		}	

		function positionItems() {
			if (!paused) target += speed;
			angle += (target - angle) / Math.max((Math.abs(options.inertia) * options.frameRate), 1.0);
			if (lastAngle !== null && Math.abs(lastAngle - angle) < 0.0005) return;

			if (target >= TWO_PI)
			{
				target -= TWO_PI;
				angle -= TWO_PI;
				lastAngle -= TWO_PI;
			}
			else if (target <= -TWO_PI)
			{
				target += TWO_PI;
				angle += TWO_PI;
				lastAngle += TWO_PI;
			}

			kids.each(positionItem);
			lastAngle = angle;
		}

		parent.data('timer', window.setInterval(positionItems, 1000 / (options.frameRate || 25)));
	});
};

$.fn.carousel.defaults =
{
	action:              'start', // What to do: start, stop, pause, resume.  The default is "start".
	                              // If you stop, pause, or resume, the other params are currently ignored.
	                              // Can also be specified like $(obj).carousel('stop') etc.

	frameRate:           25,      // The number of times per second the carousel items are moved.
	                              // Higher frame rates make for smoother motion, but at the cost of higher CPU usage
	                              // and possibly more flickering of the contents.

	rotationsPerSecond:  0.1,     // The number of times the carousel will rotate per second at full speed when not paused.
	                              // Independent of frame rate -- a carousel can spin smooth but slow.
	                              // If this number is 0, the carousel will not rotate except under the control of the
	                              // left and right buttons.

	inertia:             0.5,     // A number indicating how long it takes to get to full speed or stop.
	                              // If 0, then the carousel will start or stop instantly.

	zCenter:             1000,    // The z-index of an item that's exactly midway between the front and back.
        zRadius:             1000,    // The z-distance of the frontmost and backmost items from zCenter.

	leftButton:          null,    // A selector for the button(s) that will spin the carousel counter-clockwise.  If null, it's ignored.
	rightButton:         null,    // A selector for the button(s) that spins the carousel clockwise.  If null, it's ignored.
	ignore:              null     // A selector for elements that should not be rotated.  
	                              // The leftButton and rightButton elements are never rotated.
};

})(jQuery);

