(function ($) {
	$.fn.cjFlashySlideShow = function (options) {

		var settings = {
			// user editable settings
			xBlocks: 12,
			yBlocks: 3,
			minBlockSize: 3,
			delay: 3000,
			direction: 'left',
			style: 'normal',
			translucent: false,
			sloppy: false
		};

		var sys = {
			// system parameters
			version: '1.0',
			elem: null,
			imgs: [],
			block_dim_x: 0,
			block_dim_y: 0,
			sec_y: 0,
			sec_x: 0,
			current_img: 0,
			current_blocks: 0,
			current_direction: 'left',
			total_blocks: 0
		};

		/*
			utilities
		***************************************/
		function RandRange(a, b, f) {
			var v = a + (Math.random() * (b - a));
			return typeof f == 'undefined' ? Math.round(v) : v.toFixed(f);
		}

		/*
			core functions
		***************************************/
		// grab the next image
		function getNextImage(n) {
			if (parseInt(n, 10) + 1 < sys.imgs.length) {
				return sys.imgs[n + 1].src;
			} else {
				return sys.imgs[0].src;
			}
		}

		// reset our transitional blocks
		function resetBlocks() {
			var next_img = getNextImage(sys.current_img);
			$(sys.elem).find('div#cjFlashyTransitionTop div.cjFlashyTransitionBlock').each(function () {

				// radnomize the direction (if set)
				if (settings.direction === 'random') {
					var d = ['top', 'left', 'bottom', 'right'];
					settings.current_direction = d[RandRange(0, d.length - 1)];
					if (settings.current_direction === 'top') {
						$(this)[0]._start_top = settings.minBlockSize * -1;
						$(this)[0]._start_left = parseInt((sys.block_dim_x * $(this)[0]._x) + (sys.block_dim_x / 2) - (settings.minBlockSize / 2), 10);
					} else if (settings.current_direction === 'left') {
						$(this)[0]._start_top = parseInt((sys.block_dim_y * $(this)[0]._y) + (sys.block_dim_y / 2) - (settings.minBlockSize / 2), 10);
						$(this)[0]._start_left = settings.minBlockSize * -1;
					} else if (settings.current_direction === 'bottom') {
						$(this)[0]._start_top = $(sys.elem).height() + settings.minBlockSize;
						$(this)[0]._start_left = parseInt((sys.block_dim_x * $(this)[0]._x) + (sys.block_dim_x / 2) - (settings.minBlockSize / 2), 10);
					} else if (settings.current_direction === 'right') {
						$(this)[0]._start_top = parseInt((sys.block_dim_y * $(this)[0]._y) + (sys.block_dim_y / 2) - (settings.minBlockSize / 2), 10);
						$(this)[0]._start_left = $(sys.elem).width() + settings.minBlockSize;
					} else {
						$(this)[0]._start_top = parseInt((sys.block_dim_y * $(this)[0]._y) + (sys.block_dim_y / 2) - (settings.minBlockSize / 2), 10);
						$(this)[0]._start_left = parseInt((sys.block_dim_x * $(this)[0]._x) + (sys.block_dim_x / 2) - (settings.minBlockSize / 2), 10);
					}
				}

				$(this).css({
					'top': $(this)[0]._start_top + 'px',
					'left': $(this)[0]._start_left + 'px',
					'width': settings.minBlockSize + 'px',
					'height': settings.minBlockSize + 'px',
					'background-image': 'url(' + next_img + ')',
					'opacity': $(this)[0]._opacity
				});
			});
		}

		// handle image swaps and delay once transitional blocks are done.
		function handleBlocksDone() {
			sys.current_blocks++;
			if (sys.current_blocks === sys.total_blocks) {
				var next_img = getNextImage(sys.current_img);
				$(sys.elem).find('div#cjFlashyTransitionBottom').css({
					'background-image': 'url(' + next_img + ')',
					'background-attachment': 'scroll'
				});
				sys.current_blocks = 0;
				sys.current_img++;
				if (sys.current_img > sys.imgs.length - 1) {
					sys.current_img = 0;
				}
				resetBlocks();
				if ($(sys.elem)[0]._timer !== null) {
					clearTimeout($(sys.elem)[0]._timer);
				}
				$(sys.elem)[0]._timer = setTimeout(animateBlocks, settings.delay);
			}
		}

		// animate our transitional blocks
		function animateBlocks() {
			$(sys.elem).find('div#cjFlashyTransitionTop div.cjFlashyTransitionBlock').each(function () {
				var self = $(this)[0];
				$(self).animate({
					"top": parseInt(((sys.block_dim_y * self._y) + (sys.block_dim_y / 2) - (settings.minBlockSize / 2)) + (settings.sloppy ? RandRange(0, settings.minBlockSize) - (settings.minBlockSize / 2) : 0), 10) + "px",
					"left": parseInt(((sys.block_dim_x * self._x) + (sys.block_dim_x / 2) - (settings.minBlockSize / 2)) + (settings.sloppy ? RandRange(0, settings.minBlockSize) - (settings.minBlockSize / 2) : 0), 10) + "px"
				},
				settings.sloppy ? RandRange(350, 1250) : 650, 'linear', function () {
					$(self).animate({
						'top': self._end_top + 'px',
						'left': self._end_left + 'px',
						'width': (sys.block_dim_x * 2) + 'px',
						'height': (sys.block_dim_y * 2) + 'px',
						'opacity': 1
					},
					settings.sloppy ? RandRange(250, 850) : 650, function () {
						handleBlocksDone();
					});
				});
			});
		}

		// corrects the block's image offset to the page
		function correctOffset() {
			$(sys.elem).find('div#cjFlashyTransitionTop div.cjFlashyTransitionBlock').each(function () {
				$(this).css({
					'background-position': ($(sys.elem).offset().left - $(window).scrollLeft()) + 'px ' + ($(sys.elem).offset().top - $(window).scrollTop()) + 'px'
				});
			});
		}

		// create our transitional blocks
		function createBlocks() {
			var x, y, next_img = getNextImage(sys.current_img);
			for (y = 0; y < settings.yBlocks; y++) {
				for (x = 0; x < settings.xBlocks; x++) {

					var elem = $(sys.elem).find('div#cjFlashyTransitionTop');
					$(elem).append('<div id="cjFlashyTransitionBlock_' + y + '_' + x + '" class="cjFlashyTransitionBlock">');
					elem = $(sys.elem).find('div#cjFlashyTransitionTop div#cjFlashyTransitionBlock_' + y + '_' + x);

					$(elem)[0]._x = x;
					$(elem)[0]._y = y;

					// set up ending block location
					$(elem)[0]._end_top = parseInt(((sys.block_dim_y * y) - (sys.block_dim_y / 2)), 10);
					$(elem)[0]._end_left = parseInt(((sys.block_dim_x * x) - (sys.block_dim_x / 2)), 10);

					// determine direction
					if (settings.direction === 'random') {
						var d = ['top', 'left', 'bottom', 'right'];
						settings.current_direction = d[RandRange(0, d.length - 1)];
					}
					if (settings.current_direction === 'top') {
						$(elem)[0]._start_top = settings.minBlockSize * -1;
						$(elem)[0]._start_left = parseInt((sys.block_dim_x * x) + (sys.block_dim_x / 2) - (settings.minBlockSize / 2), 10);
					} else if (settings.current_direction === 'left') {
						$(elem)[0]._start_top = parseInt((sys.block_dim_y * y) + (sys.block_dim_y / 2) - (settings.minBlockSize / 2), 10);
						$(elem)[0]._start_left = settings.minBlockSize * -1;
					} else if (settings.current_direction === 'bottom') {
						$(elem)[0]._start_top = $(sys.elem).height() + settings.minBlockSize;
						$(elem)[0]._start_left = parseInt((sys.block_dim_x * x) + (sys.block_dim_x / 2) - (settings.minBlockSize / 2), 10);
					} else if (settings.current_direction === 'right') {
						$(elem)[0]._start_top = parseInt((sys.block_dim_y * y) + (sys.block_dim_y / 2) - (settings.minBlockSize / 2), 10);
						$(elem)[0]._start_left = $(sys.elem).width() + settings.minBlockSize;
					} else {
						$(elem)[0]._start_top = parseInt((sys.block_dim_y * y) + (sys.block_dim_y / 2) - (settings.minBlockSize / 2), 10);
						$(elem)[0]._start_left = parseInt((sys.block_dim_x * x) + (sys.block_dim_x / 2) - (settings.minBlockSize / 2), 10);
					}

					$(elem)[0]._opacity = settings.translucent ? RandRange(0.1, 0.5, 2) : 1;
					$(elem).css({
						'position': 'absolute',
						'top': $(elem)[0]._start_top + 'px',
						'left': $(elem)[0]._start_left + 'px',
						'display': 'block',
						'width': settings.minBlockSize + 'px',
						'height': settings.minBlockSize + 'px',
						'margin': '0px',
						'padding': '0px',
						'background-image': 'url(' + next_img + ')',
						'background-repeat': 'no-repeat',
						'background-position': $(sys.elem).offset().left + 'px ' + $(sys.elem).offset().top + 'px',
						'background-attachment': 'fixed',
						'opacity': $(elem)[0]._opacity
					});

					/* set up additional stylings based on style */
					if (settings.style === 'rounded') {
						$(elem).css({
							'-moz-border-radius': $(sys.elem).height() + 'px',
							'-webkit-border-radius': $(sys.elem).height() + 'px'
						});
					}
				}
			}

			// corrects the images offsets if the user scrolls the page
			$(window).scroll(function () {
				correctOffset();
			});

			// corrects the images offsets if the user resizes the page
			$(window).resize(function () {
				correctOffset();
			});
		}

		/* 
			initialize our plug-in
		***************************************/
		function init() {

			// verify the user settings
			settings.yBlocks = typeof settings.yBlocks !== "number" ? 3 : settings.yBlocks < 0 ? 1 : settings.yBlocks;
			settings.xBlocks = typeof settings.xBlocks !== "number" ? 3 : settings.xBlocks < 0 ? 1 : settings.xBlocks;
			settings.minBlockSize = typeof settings.minBlockSize !== "number" ? 5 : settings.minBlockSize < 0 ? 0 : settings.minBlockSize;
			if ($(sys.elem).width() > $(sys.elem).height() && settings.minBlockSize > $(sys.elem).width()) {
				settings.minBlockSize = $(sys.elem).width();
			} else if (settings.minBlockSize > $(sys.elem).height()) {
				settings.minBlockSize = $(sys.elem).height();
			}
			settings.delay = typeof settings.delay !== "number" ? 3000 : settings.delay < 0 ? 0 : settings.delay;
			if (settings.style !== 'rounded') {
				settings.style = 'normal';
			}
			settings.translucent = typeof settings.translucent !== "boolean" ? false : settings.translucent;
			settings.sloppy = typeof settings.sloppy !== "boolean" ? false : settings.sloppy;
			if (settings.direction !== 'top' && settings.direction !== 'left' && settings.direction !== 'bottom' && settings.direction !== 'right' && settings.direction !== 'random' && settings.direction !== 'none') {
				settings.direction = 'left';
			} else if (settings.direction === 'random') {
				var d = ['top', 'left', 'bottom', 'right'];
				settings.current_direction = d[RandRange(0, d.length - 1)];
			} else {
				settings.current_direction = settings.direction;
			}

			// setup some initial variables
			sys.imgs = $(sys.elem).find('img');
			sys.block_dim_x = Math.ceil($(sys.elem).width() / settings.xBlocks);
			sys.block_dim_y = Math.ceil($(sys.elem).height() / settings.yBlocks);
			sys.offset_x = parseInt((parseInt($(sys.elem).css("width"), 10) - (sys.block_dim_x * settings.xBlocks)) / 2, 10);
			sys.offset_y = parseInt((parseInt($(sys.elem).css("height"), 10) - (sys.block_dim_y * settings.yBlocks)) / 2, 10);
			sys.total_blocks = settings.xBlocks * settings.yBlocks;
			sys.current_img = 0;
			sys.current_blocks = 0;

			$(sys.elem).append('<div id="cjFlashyTransitionBottom">');
			$(sys.elem).find('div#cjFlashyTransitionBottom').css({
				'position': 'absolute',
				'top': '0px',
				'left': '0px',
				'display': 'block',
				'width': $(sys.elem).css("width"),
				'height': $(sys.elem).css("height"),
				'margin': '0px',
				'padding': '0px',
				'background-image': 'url("' + sys.imgs[sys.current_img].src + '")',
				'background-repeat': 'no-repeat',
				'background-attachment': 'scroll',
				'z-index': 1,
				'overflow': 'hidden'
			});

			$(sys.elem).append('<div id="cjFlashyTransitionTop">');
			$(sys.elem).find('div#cjFlashyTransitionTop').css({
				'position': 'absolute',
				'top': sys.offset_y + 'px',
				'left': sys.offset_x + 'px',
				'display': 'block',
				'width': sys.block_dim_x * settings.xBlocks + 'px',
				'height': sys.block_dim_y * settings.yBlocks + 'px',
				'margin': '0px',
				'padding': '0px',
				'z-index': 2,
				'overflow': 'hidden'
			});

			createBlocks();
			$(sys.elem)[0]._timer = setTimeout(animateBlocks, settings.delay);
		}

		/* 
			set up any user passed variables
		***************************************/
		if (options) {
			$.extend(settings, options);
		}

		/* 
			begin
		***************************************/
		return this.each(function () {
			sys.elem = this;
			if (sys.elem) {
				init();
			}
		});
	};
})(jQuery);
