(function($j)
{
	$j.fn.slider = function(groupOptions)
	{
		groupOptions = $j.extend({}, $j.fn.slider.defaults, groupOptions);
		
		return this.each(function()
		{
			var that = $j(this);
			
			var slider = that.data('slider');
			
			if (slider)
			{
				return slider;
			}
			
			var options = $j.meta ? $j.extend({}, groupOptions, that.data()) : groupOptions;
			
			slider = new Slider(that, options);
			
			that.data('slider', slider);
			
			return slider;
		});
	};
	
	$j.fn.getSlider = function()
	{
		var slider = $j(this).closest('.mod-slider');
		
		return slider.data('slider');
	};
	
	function Slider(module, options)
	{
		var slider = this;
		
		this.options = options;
		
		this.container = module.find('.mod_slider');
		this.slideContainer = this.container.find('.mod_slider_container');
		
		if (!this.slideContainer.length)
		{
			return;
		}
		
		this.title = this.container.find('.mod_slider_title');
		this.slideList = this.slideContainer.find('.mod_slider_slides');
		this.slides = this.slideList.find('li');
		this.pagingItems = this.container.find('.mod_slider_paging li');
		this.arrows = this.container.find('.mod_slider_arrow_back, .mod_slider_arrow_forward');
		
		var controlClick = function(callback)
		{
			return function()
			{
				if (!slider.slideList.find('li').andSelf().filter(':animated').length)
				{
					slider.clearTimeout();
					
					callback.call(this);
					
					slider.setTimeout();
				}
				
				return false;
			};
		};
		
		this.pagingItems.click(controlClick(function()
		{
			slider.goTo(slider.pagingItems.index(this), false, true);
		}));
		
		this.container.find('.mod_slider_arrow_back').click(controlClick(function()
		{
			slider.goBack();
		}));
		
		this.container.find('.mod_slider_arrow_forward').click(controlClick(function()
		{
			slider.goForward();
		}));
		
		this.count = this.slides.length;
		
		this.container.addClass('mod_slider_effect_' + this.options.effect);
		
		this.isSwipe = this.options.effect === 'horizontal_swipe' || this.options.effect === 'vertical_swipe';
		
		this.shouldWrap = this.options.wrap && this.isSwipe;
		
		if (this.shouldWrap)
		{
			this.slideList.append(this.slides.eq(0).clone());
			this.slideList.prepend(this.slides.eq(-1).clone());
		}
		
		if (this.isSwipe)
		{
			var count = this.count + 2;
		}
		
		switch (this.options.effect)
		{
			case 'horizontal_swipe':
				
				this.slideWidth = this.slides.width();
				
				this.slideList.width(count * this.slideWidth);
				
				break;
			
			case 'vertical_swipe':
				
				this.slideHeight = this.slides.height();
				
				this.slideList.height(count * this.slideHeight);
				
				break;
		}
		
		if (this.options.crossPageLinking && location.hash)
		{
			var index = parseInt(location.hash, 10);
			
			this.goTo(index, true);
		}
		else
		{
			this.goTo(this.options.firstSlide, true);
		}
		
		var arrowsOnSlideContainer = true,
			slideContainerOffset = this.slideContainer.offset();
		
		slideContainerOffset.width = this.slideContainer.width();
		slideContainerOffset.height = this.slideContainer.height();
		
		this.arrows.each(function()
		{
			var offset = $j(this).offset();
			
			if (!(offset.top > slideContainerOffset.top && offset.top < slideContainerOffset.top + slideContainerOffset.height &&
				offset.left > slideContainerOffset.left && offset.left < slideContainerOffset.left + slideContainerOffset.width))
			{
				arrowsOnSlideContainer = false;
				
				return false;
			}
		});
		
		if (arrowsOnSlideContainer)
		{
			this.arrows.hide();
			
			this.container.find('.mod_slider_hover_area').mouseenter(function(e)
			{
				slider.arrows.show();
			})
			.mouseleave(function(e)
			{
				slider.arrows.hide();
			});
		}
		
		if (this.options.auto)
		{
			this.setTimeout();
			
			if (this.options.autoSlideStopOnHover)
			{
				this.slides.hover(function()
				{
					slider.clearTimeout();
				},
				function()
				{
					slider.setTimeout();
				});
			}
		}
	}
	
	Slider.prototype =
	{
		goTo: function(index, noEffect, pagingUsed)
		{
			if (!noEffect && index == this.currentIndex)
			{
				return;
			}
			
			this.title.hide();
			
			var bound = 'inside';
			
			// Check the bounds.
			if (index < 0)
			{
				bound = 'lower';
				
				index = this.count - 1;
			}
			else if (index >= this.count)
			{
				bound = 'higher';
				
				index = 0;
			}
			
			var previousIndex = this.currentIndex || 0;
			
			this.currentIndex = index;
			
			var slide = this.slides.eq(index);
			
			this.pagingItems.removeClass('active').eq(index).addClass('active');
			
			if (this.shouldWrap)
			{
				var shouldWrap = !noEffect && bound !== 'inside'; 
				
				if (shouldWrap)
				{
					index = bound === 'higher' ? this.count + 1 : 0;
				}
				else
				{
					index++;
				}
			}
			
			var slider = this;
			
			switch (this.options.effect)
			{
				case 'fade':
					
					this.slides.css('z-index', 100);
					this.slides.eq(previousIndex).css('z-index', 101);
					
					// What higher z-index values, so designers can still use z-index in their designs.
					if (!noEffect)
					{
						slide.hide().css('z-index', 102).fadeIn(this.options.effectDuration, function()
						{
							slider.setTimeout();
						});
					}
					
					break;
				
				case 'horizontal_swipe':
					
					var style =
					{
						left: -1 * this.slideWidth * index
					};
					
					break;
				
				case 'vertical_swipe':
					
					var style =
					{
						top: -1 * this.slideHeight * index
					};
					
					break;
			}
			
			if (this.isSwipe)
			{
				if (!noEffect)
				{
					if (shouldWrap)
					{
						this.slideList.animate(style, this.options.effectDuration, function()
						{
							slider.goTo(slider.currentIndex, true);
						});
					}
					else
					{
						this.slideList.animate(style, this.options.effectDuration, function()
						{
							slider.setTimeout();
						});
					}
				}
				else
				{
					this.slideList.css(style);
					
					this.setTimeout();
				}
			}
			
			this.title.html(slide.find('div').html());
			
			if (!noEffect)
			{
				this.title.fadeIn(this.options.effectDuration);
			}
			else
			{
				this.title.show();
			}
		},
		
		goBack: function()
		{
			this.goTo(this.currentIndex - 1);
		},
		
		goForward: function()
		{
			this.goTo(this.currentIndex + 1);
		},
		
		clearTimeout: function()
		{
			if (this.autoTimeout)
			{
				clearTimeout(this.autoTimeout);
				
				this.autoTimeout = null;
			}
		},
		
		setTimeout: function()
		{
			var slider = this;
			
			this.clearTimeout();
			
			this.autoTimeout = setTimeout(function()
			{
				slider.goForward();
			},
			this.options.autoInterval);
		}
	};
	
	$j.fn.slider.defaults =
	{
		// 0 = first, since we are using indexes.
		firstSlide: 0,
		auto: true,
		autoInterval: 3000,
		// When an control element is used to determine the slide
		// to go to, then the slider should stop.
		autoSlideStopOnHover: true,
		// Should there be a hash added after the URL's and can a
		// hash determine what the start slide should be?
		crossPageLinking: true,
		wrap: true,
		effect: 'fade',
		effectDuration: 2000
	};
})
(jQuery);