(function($) {

	var defaults = {
		'images': [],
		'width': 800,
		'height': 250,
		'speed': 1500,
		'pause': 4000,
		'bgcolor': '#000000',
		'indicator': false
	};
	
	var animations = [
		'SCROLL',
		'ROLL',
		'FREE',
		'SPLIT',
		'CROSSFADE',
		'FADEOUT',
		'FADEINOUT',
		'GROW',
		'SPLIT_QUARTER',
		'SPLIT_EIGHT'
	];
	
	var directions = {
		'SCROLL': ['NORTH', 'EAST', 'SOUTH', 'WEST'],
		'ROLL': ['NORTH', 'EAST', 'SOUTH', 'WEST'],
		'FREE': ['NORTH', 'EAST', 'SOUTH', 'WEST'],
		'SPLIT': ['WEST_EAST', 'SOUTH_NORTH', 'WEST_EAST_SPLIT', 'SOUTH_NORTH_SPLIT'],
		'CROSSFADE': [],
		'FADEOUT': [],
		'FADEINOUT': [],
		'GROW': [],
		'SPLIT_QUARTER': ['WEST_EAST', 'EAST', 'WEST'],
		'SPLIT_EIGHT': ['WEST_EAST', 'EAST', 'WEST']
	};

	var currentImageCount = 0;
	var currentImage;
	var lastImage;
	var lastImageClone;
	var $container;
	var animationTimeout;

	var methods = {
		init: function(options) {

			defaults = $.extend(defaults, options);

			if (defaults.indicator) {
				for (var i = 1; i <= defaults.images.length; ++i) {
					$('<a href="#"></a>')
						.addClass(defaults.indicator.elementClass)
						.appendTo(defaults.indicator.container)
						.bind('click', {
							'elementPos': i
						}, function(e) {
							e.preventDefault();

							methods.skip(false, e.data.elementPos);
						})
					;
				}
				
				$('.' + defaults.indicator.elementClass, defaults.indicator.container)
					.eq(0)
					.addClass(defaults.indicator.elementClassActive)
				;
			}

			return this.each(function() {
				$container = $(this);

				var currentImagePreload = new Image();
				$(currentImagePreload).attr({
					'src': defaults.images[currentImageCount].imagefile
				}).load(function() {
					if (defaults.images[1].speed) {
						defaults.speed = parseInt(defaults.images[1].speed);
					}
					if (defaults.images[1].pause) {
						defaults.pause = parseInt(defaults.images[1].pause);
					}
					
					if (defaults.images[1].animation == 'RANDOM') {
						defaults.images[1].animation = methods.getRandomAnimation();
					}

					switch (defaults.images[1].animation) {

						case 'GROW':
							currentImage = methods.generateImgImage();

							currentImage.css({
								'left': '0px',
								'top': '0px'
							});

							break;

						default:
							currentImage = methods.generateDivImage();

							currentImage.css({
								'left': '0px',
								'top': '0px'
							});

							break;
					}

					animationTimeout = setTimeout(function() {
						methods.animateNext();
					}, defaults.pause);
				});
			});
		},
		animateNext: function(directImageCount) {
			++currentImageCount;
			
			if (directImageCount) {
				currentImageCount = directImageCount;
			}

			if (currentImageCount >= defaults.images.length) {
				currentImageCount = 0;
			}
			
			if (defaults.indicator) {
				$('.' + defaults.indicator.elementClassActive, defaults.indicator.container)
					.removeClass(defaults.indicator.elementClassActive)
				;
				
				$('.' + defaults.indicator.elementClass, defaults.indicator.container)
					.eq(currentImageCount)
					.addClass(defaults.indicator.elementClassActive)
				;
			}
			
			if (defaults.images[currentImageCount].speed) {
				defaults.speed = parseInt(defaults.images[currentImageCount].speed);
			}
			if (defaults.images[currentImageCount].pause) {
				defaults.pause = parseInt(defaults.images[currentImageCount].pause);
			}
			//alert(defaults.speed + ' : ' + defaults.pause);
			
			lastImage = currentImage;

			var currentImagePreload = new Image();
			$(currentImagePreload).attr({
				'src': defaults.images[currentImageCount].imagefile
			}).load(function() {
				// Get random animation
				if (defaults.images[currentImageCount].animation == 'RANDOM') {
					defaults.images[currentImageCount].animation = methods.getRandomAnimation();
				}

				// Get random direction
				if (defaults.images[currentImageCount].direction == 'RANDOM' || (!defaults.images[currentImageCount].direction && directions[defaults.images[currentImageCount].animation].length > 0)) {
					defaults.images[currentImageCount].direction = methods.getRandomDirection(defaults.images[currentImageCount].animation);
				}
				//alert(defaults.images[currentImageCount].animation + ' : ' + defaults.images[currentImageCount].direction);
				
				// Generate the image element
				switch (defaults.images[currentImageCount].animation) {
					case 'SPLIT':
						lastImageClone = lastImage.clone();
						lastImageClone.appendTo($container);
						
						lastImageClone.css({
							'zIndex': 10
						});
					case 'FREE':
					case 'FADEOUT':
					case 'FADEINOUT':
						lastImage.css({
							'zIndex': 10
						});
						currentImage.css({
							'zIndex': 9
						});
					case 'CROSSFADE':
						lastImage.css({
							'opacity': 1
						});
					case 'SCROLL':
					case 'ROLL':
					case 'SPLIT_QUARTER':
					case 'SPLIT_EIGHT':
						currentImage = methods.generateDivImage();

						break;

					case 'GROW':
						currentImage = methods.generateImgImage();
						
						lastImage.css({
							'zIndex': 10
						});
						currentImage.css({
							'zIndex': 9
						});

						break;

					default: // fallback
						defaults.images[currentImageCount].animation = 'CROSSFADE';
						
						lastImage.css({
							'opacity': 1
						});
						
						currentImage = methods.generateDivImage();
						
						break;
				}

				// Position the image element inside the container
				switch (defaults.images[currentImageCount].direction) {
					case 'WEST':
						currentImage.css({
							'left': defaults.width + 'px',
							'top': '0px'
						});

						break;

					case 'EAST':
						currentImage.css({
							'left': -(defaults.width) + 'px',
							'top': '0px'
						});

						break;

					case 'SOUTH':
						currentImage.css({
							'left': '0px',
							'top': -(defaults.height) + 'px'
						});

						break;

					case 'NORTH':
						currentImage.css({
							'left': '0px',
							'top': defaults.height + 'px'
						});

						break;

					case 'WEST_EAST':
					case 'SOUTH_NORTH':
						currentImage.css({
							'left': '0px',
							'top': '0px'
						});

						break;

					default:
						currentImage.css({
							'left': '0px',
							'top': '0px'
						});

						break;
				}

				var lastToLeft;
				var lastToTop;
				var lastCloneToLeft;
				var lastCloneToTop;

				// Animate the elements
				switch (defaults.images[currentImageCount].animation) {
					case 'SCROLL':
						switch (defaults.images[currentImageCount].direction) {
							case 'WEST':
								lastToLeft = -(defaults.width);
								lastToTop = 0;

								break;

							case 'EAST':
								lastToLeft = defaults.width;
								lastToTop = 0;

								break;

							case 'SOUTH':
								lastToLeft = 0;
								lastToTop = defaults.height;

								break;

							case 'NORTH':
								lastToLeft = 0;
								lastToTop = -(defaults.height);

								break;
						}

						lastImage.animate({
							'left': lastToLeft + 'px',
							'top': lastToTop + 'px'
						}, defaults.speed, function() {
							lastImage.empty().remove();
						});

						currentImage.animate({
							'left': '0px',
							'top': '0px'
						}, defaults.speed, function() {
							animationTimeout = setTimeout(function() {
								methods.animateNext();
							}, defaults.pause);
						});

						break;

					case 'ROLL':
						currentImage.animate({
							'left': '0px',
							'top': '0px'
						}, defaults.speed, function() {
							lastImage.empty().remove();
							animationTimeout = setTimeout(function() {
								methods.animateNext();
							}, defaults.pause);
						});

						break;

					case 'FREE':
						switch (defaults.images[currentImageCount].direction) {
							case 'WEST':
								lastToLeft = -(defaults.width);
								lastToTop = 0;

								break;

							case 'EAST':
								lastToLeft = defaults.width;
								lastToTop = 0;

								break;

							case 'SOUTH':
								lastToLeft = 0;
								lastToTop = defaults.height;

								break;

							case 'NORTH':
								lastToLeft = 0;
								lastToTop = -(defaults.height);

								break;
						}
						
						currentImage.css({
							'left': '0px',
							'top': '0px'
						});

						lastImage.animate({
							'left': lastToLeft + 'px',
							'top': lastToTop + 'px'
						}, defaults.speed, function() {
							lastImage.empty().remove();
							animationTimeout = setTimeout(function() {
								methods.animateNext();
							}, defaults.pause);
						});

						break;

					case 'SPLIT':
						switch (defaults.images[currentImageCount].direction) {
							case 'WEST_EAST':
								lastImage.css({
									'width': (defaults.width / 2) + 'px'
								});
								lastImageClone.css({
									'width': (defaults.width / 2) + 'px',
									'left': (defaults.width / 2) + 'px',
									'backgroundPosition': 'right top'
								});

								lastToLeft = -(defaults.width / 2);
								lastToTop = 0;

								lastCloneToLeft = defaults.width;
								lastCloneToTop = 0;

								break;

							case 'SOUTH_NORTH':
								lastImage.css({
									'height': (defaults.height / 2) + 'px'
								});
								lastImageClone.css({
									'height': (defaults.height / 2) + 'px',
									'top': (defaults.height / 2) + 'px',
									'backgroundPosition': 'left bottom'
								});

								lastToLeft = 0;
								lastToTop = -(defaults.height / 2);

								lastCloneToLeft = 0;
								lastCloneToTop = defaults.height;

								break;

							case 'WEST_EAST_SPLIT':
								lastImage.css({
									'height': (defaults.height / 2) + 'px'
								});
								lastImageClone.css({
									'height': (defaults.height / 2) + 'px',
									'top': (defaults.height / 2) + 'px',
									'backgroundPosition': 'left bottom'
								});

								lastToLeft = -defaults.width;
								lastToTop = 0;

								lastCloneToLeft = defaults.width;
								lastCloneToTop = defaults.height / 2;

								break;

							case 'SOUTH_NORTH_SPLIT':
								lastImage.css({
									'width': (defaults.width / 2) + 'px'
								});
								lastImageClone.css({
									'width': (defaults.width / 2) + 'px',
									'left': (defaults.width / 2) + 'px',
									'backgroundPosition': 'right top'
								});

								lastToLeft = 0;
								lastToTop = -defaults.height;

								lastCloneToLeft = defaults.width / 2;
								lastCloneToTop = defaults.height;

								break;
						}

						lastImage.animate({
							'left': lastToLeft + 'px',
							'top': lastToTop + 'px'
						}, defaults.speed, function() {
							lastImage.empty().remove();
						});

						lastImageClone.animate({
							'left': lastCloneToLeft + 'px',
							'top': lastCloneToTop + 'px'
						}, defaults.speed, function() {
							lastImageClone.empty().remove();
							animationTimeout = setTimeout(function() {
								methods.animateNext();
							}, defaults.pause);
						});

						break;

					case 'CROSSFADE':
						currentImage.css({
							'opacity': 0
						}).animate({
							'opacity': 1
						}, defaults.speed);

						// NO Break!
					case 'FADEOUT':
						lastImage.animate({
							'opacity': 0
						}, defaults.speed, function() {
							lastImage.empty().remove();

							animationTimeout = setTimeout(function() {
								methods.animateNext();
							}, defaults.pause);
						});

						break;
						
					case 'FADEINOUT':
						var currentBgColor = $container.css('backgroundColor');

						if (defaults.bgcolor) {
							$container.css({
								'backgroundColor': defaults.bgcolor
							});
						}

						currentImage.css({
							'opacity': 0
						});

						lastImage.animate({
							'opacity': 0
						}, defaults.speed, function() {
							lastImage.empty().remove();

							currentImage.animate({
								'opacity': 1
							}, defaults.speed, function() {
								$container.css({
									'backgroundColor': currentBgColor
								});


								animationTimeout = setTimeout(function() {
									methods.animateNext();
								}, defaults.pause);
							});
						});

						break;

					case 'GROW':
						lastImage.animate({
							'width': (defaults.width * 2) + 'px',
							'height': (defaults.height * 2) + 'px',
							'left': -(defaults.width / 2) + 'px',
							'top': -(defaults.height / 2) + 'px',
							'opacity': 0
						}, defaults.speed, function() {
							lastImage.empty().remove();
							
							animationTimeout = setTimeout(function() {
								methods.animateNext();
							}, defaults.pause);
						});

						break;

					case 'SPLIT_QUARTER':
						// Reposition current image and ignore former settings
						currentImage.css({
							'left': '0px',
							'top': '0px'
						});

						methods.animateParts(4);

						break;

					case 'SPLIT_EIGHT':
						// Reposition current image and ignore former settings
						currentImage.css({
							'left': '0px',
							'top': '0px'
						});

						methods.animateParts(8);

						break;
				}
			});
		},
		generateDivImage: function() {
			return $('<div />').css({
				'width': defaults.width + 'px',
				'height': defaults.height + 'px',
				'background': 'transparent url(' + defaults.images[currentImageCount].imagefile + ') no-repeat left top',
				'position': 'absolute'
			}).appendTo($container);
		},
		generateImgImage: function() {
			return $('<img />').css({
				'width': defaults.width + 'px',
				'height': defaults.height + 'px',
				'position': 'absolute'
			}).attr({
				'src': defaults.images[currentImageCount].imagefile
			}).appendTo($container);
		},
		stop: function() {
			if (lastImage) {
				lastImage.stop();
			}
			if (lastImageClone) {
				lastImageClone.stop();
			}
			if (currentImage) {
				currentImage.stop();
			}
			
			clearTimeout(animationTimeout);
		},
		getRandomAnimation: function() {
			return animations[Math.floor(Math.random() * animations.length)];
		},
		getRandomDirection: function(animation) {
			return directions[animation][Math.floor(Math.random() * directions[animation].length)];
		},
		animateParts: function(partsNumber) {
			var parts = [];

			for (var q = 1; q <= partsNumber; ++q) {
				parts[q] = lastImage.clone().appendTo($container);

				parts[q].css({
					'width': (defaults.width / partsNumber) + 'px',
					'left': ((defaults.width / partsNumber) * (q - 1)) + 'px',
					'backgroundPosition': -((defaults.width / partsNumber) * (q - 1)) + 'px 0px'
				}).addClass('rsAnimatorPart_' + ((q % 2) ? 'odd' : 'even') + ' rsAnimatorPart_' + q);
			}

			lastImage.empty().remove();

			var qLeftOdd;
			var qLeftEven;

			switch (defaults.images[currentImageCount].direction) {
				case 'WEST':
					qLeftOdd = qLeftEven = -(defaults.width / partsNumber);

					break;

				case 'EAST':
					qLeftOdd = qLeftEven = defaults.width;

					break;

				case 'WEST_EAST':
					qLeftOdd = defaults.width;
					qLeftEven = -(defaults.width / partsNumber);

					break;
			}

			$('.rsAnimatorPart_odd').animate({
				'left': qLeftOdd + 'px'
			}, defaults.speed, function() {
				$(this).empty().remove();
			});

			$('.rsAnimatorPart_even').animate({
				'left': qLeftEven + 'px'
			}, defaults.speed, function() {
				$(this).empty().remove();

				if ($(this).hasClass('rsAnimatorPart_' + partsNumber)) {
					animationTimeout = setTimeout(function() {
						methods.animateNext();
					}, defaults.pause);
				}
			});
		},
		skip: function(direction, skipToNumber) {
			if (lastImage) {
				lastImage.stop(true, true);
			}
			if (lastImageClone) {
				lastImageClone.stop(true, true);
			}
			if (currentImage) {
				currentImage.stop(true, true);
			}
			
			clearTimeout(animationTimeout);
			
			if (direction == 'backward') {
				currentImageCount -= 2;
			}
			
			if (skipToNumber) { 
				methods.animateNext(skipToNumber - 1);
			} else {
				methods.animateNext();
			}
			
			
		}
	};

	$.fn.rsanimator = function(method) {

		if (methods[method]) {
			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
		} else if (typeof method === 'object' || !method) {
			return methods.init.apply(this, arguments);
		} else {
			$.error('Method ' +  method + ' does not exist on jQuery.rsanimator');

			return false;
		}
	};

})(jQuery);

