(function($){

	$.fn.cycle = function(settings) {
		var defaults = {
			delay      : 5,    // seconds
			fade_speed : .5,
			selection  : {
				element      : '',
				item_class   : '',
				text_element : ''
			}
		};
		settings = $.extend(defaults, settings);

		return this.each(function() {
			Cycle(this, settings);
		});
	};

	function Cycle(element, settings)
	{
		return this instanceof Cycle ? this.init(element, settings) : new Cycle(element, settings);
	}
	
	$.extend(Cycle.prototype, {
		init : function(element, settings) {
			this.element  = element;
			this.settings = settings;
			this.timer    = null;
			this.locked   = false;
			this.hovering = false;
			
			// make sure the delay is at least twice the fade_speed
			if(this.settings.fade_speed*2 > this.settings.delay)
			{
				this.settings.delay = this.settings.fade_speed*2;
			}
			
			var self = this;

			this.images      = [];
			this.image_index = 0;
			$('img', this.element).each(function() {
				self.images.push(this);

				// hide all images but the first
				if(self.image_index > 0)
				{
					$(this).hide();
					$(this).css('opacity', 0);
				}
				self.image_index++;
			});
			this.image_index = 0;

			// add items to selection and link to images
			if(typeof this.settings.selection == 'object')
			{
				var images_length = this.images.length;
				for(var i = 0; i < images_length; i++)
				{
					$(this.settings.selection.element).append(
						$('<div></div>')
						.addClass(this.settings.selection.item_class)
						.addClass('item_'+i)
						.bind('mouseenter mouseout', function(e) {
							self.item_hover_callback(e, this);
						})
						.bind('click', function(e) {
							self.item_click_callback(e, this);
						})
					);
				}
				
				// activate the first selection item
				$('.item_0', this.settings.selection.element).addClass('active');
				if(this.settings.selection.text_element != '')
				{
					$(this.settings.selection.text_element).html($(this.images[0]).attr('alt'));
				}
			}

			this.timer = setTimeout(function() { self.cycle(); }, self.settings.delay*1000);
		},
		item_hover_callback : function(e, item) {
			var matches = $(item).attr('class').match(/item_(\d+)/g);
			var index = matches[0].replace('item_','');

			$(item).toggleClass('hover');
			if(this.settings.selection.text_element != '')
			{
				if(e.type == 'mouseenter')
				{
					$(this.settings.selection.text_element).html($(this.images[index]).attr('alt'));
					this.hovering = true;
				}
				else
				{
					$(this.settings.selection.text_element).html($(this.images[this.image_index]).attr('alt'));
					this.hovering = false;
				}
			}
		},
		item_click_callback : function(e, item) {
			var matches = $(item).attr('class').match(/item_(\d+)/g);
			var index = matches[0].replace('item_','');

			this.cycle(parseInt(index));
		},
		cycle : function(next_index) {
			if(this.locked) return;
			this.lock();

			// clear timer
			if(this.timer != null)
			{
				clearTimeout(this.timer);
				this.timer = null;
			}

			if(typeof next_index == 'undefined')
			{
				next_index = this.image_index+1;
				if(next_index >= this.images.length)
				{
					next_index = 0;
				}
			}

			var self = this;

			if(next_index != this.image_index)
			{
				// fade out old image
				$(self.images[self.image_index]).fadeTo(self.settings.fade_speed*1000, 0.0, function() {
					$(this).hide();

					if(typeof self.settings.selection == 'object')
					{
						// unselect old selection item
						$('.item_'+self.image_index, self.settings.selection.element).removeClass('active');

						// select new selection item
						$('.item_'+next_index, self.settings.selection.element).addClass('active');
						
						if(!self.hovering)
						{
							$(self.settings.selection.text_element).html($(self.images[next_index]).attr('alt'));
						}
					}

					// fade in new image
					$(self.images[next_index]).show();
					$(self.images[next_index]).fadeTo(self.settings.fade_speed*1000, 1.0, function() {
						self.image_index = next_index;

						// reset timer
						self.timer = setTimeout(function() { self.cycle(); }, self.settings.delay*1000);
						self.unlock();
					});
				});
			}
			else
			{
				// reset timer
				this.timer = setTimeout(function() { self.cycle(); }, self.settings.delay*1000);
				this.unlock();
			}
		},
		lock : function() {
			this.locked = true;
		},
		unlock : function() {
			this.locked = false;
		}
	});
})(jQuery);
