(function($) {	
	
	// z-indexes for various items
	
	var Z_DARK = 1000;
	var Z_LIST_IMAGES = 1002;
	var Z_LIST_IMAGES_TOP = 1003;
	var Z_OUTER_CONTAINER = 1005;
	var Z_CURRENT_IMAGE = 1010;
	var Z_BUTTONS = 1020;
	var Z_LOADING_IMAGE = 1050;
	var Z_LARGE_IMAGE = 1070;
	var Z_CLOSE_BUTTON = 1080;
	
	$.fn.kaStack = function(options) {
		$(this).each(function() {
			var defaults = {
				clicker: null,						// an alternate element to bind the stack to
				per_page: 4,						// number of images per page. surprisingly doesnt break if you change.
				per_row: 2,							// number of images per row. breaks if you change.
				speed: 1000,						// base speed
				margin: 20,							// margin between images
				width: 290,							// image width
				height: 200,						// image height
				large_width: 800,					// expanded image width
				large_height: 556,					// expanded image height
				back_opacity: 0.8,					// opacity of dark background
				elements_position_type: 'fixed',	// use 'fixed' or 'absolute' - fixed will cause the stack display to scroll with the page.
													// keep as fixed because absolute isnt actually implemented.
				padding: 5,
				link: null
			};
									
			var opts = $.extend({}, defaults, options || {});
			opts.orig_height = opts.height;
			opts.orig_width = opts.width;

			// Useful elements
			var $ul = $(this);
			var $top_li = $ul.find('li:first');
			
			var $current_page_container = null;
			var $outer_container = null;		
			var $buttons_div = null;
			var $darken = null;
			var $open_image_element = null;
			var $original_image_element = null;
			var $close_button = null;
			
			// If we have a separate clicker specified, we use that. Otherwise, the top li.
			if (opts.clicker) {
				var $clicker = opts.clicker;
			}
			else {
				var $clicker = $top_li;
			}

			var pages = [];
			var $images = $ul.find('li a');
			if ($images.length > 1) {
				(function($images){$clicker.click(function() { open_list($images); });})($images);
			}
			else {
				$clicker.click(function() {
					open_single_image($ul.find('a'));
					return false;
				});
			}
			
			var current_page = 0;
			
			var num_pages = Math.ceil($images.length / opts.per_page);
			
			var image_open = false;
			
			var page_centre_x_c = 0;
			var page_centre_y_c = 0;
			
			//----------------------------------------
			
			function speed(factor) {
				return opts.speed * factor;
			}
			
			function darken() {
				$darken = $('<div></div>');
				$darken.css({
					'background-color': 'black',
					'opacity': 0,
					'width': $(document).width(),
					'height': $(document).height(),
					'top': 0,
					'left': 0,
					'z-index': Z_DARK,
					'position': 'absolute'
				})
				$('body').append($darken);
				$darken.animate({'opacity': opts.back_opacity}, speed(1));
			}
			
			function close_list() {
				$outer_container.fadeOut(speed(1), function() { $(this).remove(); });
				$darken.fadeOut(speed(1), function() { $(this).remove(); });
			}
			
			function fill_page(page) {
				if (!pages[page]) pages[page] = [];
				else return;
				for (var i = page * opts.per_page; i < (page * opts.per_page) + opts.per_page; i++) {
					var $img = $images.eq(i).clone();
					$img.css({
						'padding': opts.padding,
						'background': 'white',
						'display': 'block',
						'width': opts.width,
						'height': opts.height
					});
					$img.find('img').width(opts.width).height(opts.height).css({'display': 'block'});
					pages[page].push({
						'element': $img,
						'width': opts.width,
						'height': opts.height
					});
				}
			}
			
			function open_list($images) {
				
				// Before we begin, we need to check that the images will actually fit in the browser window. If not, they will have to be scaled down :(

				var  buffer = 250;
				if ((opts.orig_height + opts.padding * 2) * (opts.per_page / opts.per_row) + opts.margin * ((opts.per_page / opts.per_row) - 1) + buffer > $(window).height()) {
					var original_height = opts.orig_height;
					var win_height = $(window).height() - buffer - (opts.padding * 2) * (opts.per_page / opts.per_row) + opts.margin * ((opts.per_page / opts.per_row) - 1);
					var max_height = Math.floor(win_height / (opts.per_page / opts.per_row));
					var factor = original_height / max_height;
					opts.width = opts.orig_width / factor;
					opts.height = max_height;
				}
				
				current_page = 0;
				fill_page(0);
				$outer_container = $('<div></div>');
				$outer_container.click(function() {
					if (image_open) {
						close_image();
					}
					else {
						close_list();
					}
					return false;
				})
				$outer_container.css({
					'position': 'absolute',
					'z-index': Z_OUTER_CONTAINER,
					'top': 0,
					'left': 0,
					'width': $(document).width(),
					'height': $(document).height()
				});
				$('body').append($outer_container);
				
				darken();
				show_buttons();
				show_page(0, 'unstack');
				return false;
			}
			
			function next_page() {
				current_page++;
				update_buttons();
				fill_page(current_page);
				show_page(current_page, 'scroll_left');
			}
			
			function prev_page() {
				if (current_page)
					current_page--;
				fill_page(current_page);
				update_buttons();
				show_page(current_page, 'scroll_right');
			}
			
			function update_buttons() {
				var $prev = $buttons_div.find('.prev');
				var $next = $buttons_div.find('.next');
				if (current_page && $prev.hasClass('hidden')) {
					$prev.css({'visibility': 'visible', 'opacity': 0}).animate({'opacity': 1}, speed(0.5)).removeClass('hidden');
				}
				if (!current_page) {
					$prev.animate({'opacity': 0}, speed(0.5), function() { $(this).css({'visibility': 'hidden'}) }).addClass('hidden').css({'display': 'inline'});
				}
				if (current_page == num_pages - 1) {
					$next.animate({'opacity': 0}, speed(0.5), function() { $(this).css({'visibility': 'hidden'}) }).addClass('hidden').css({'display': 'inline'});
				}
				if (current_page < num_pages - 1 && $next.hasClass('hidden')) {
					$next.css({'visibility': 'visible', 'opacity': 0}).animate({'opacity': 1}, speed(0.5)).removeClass('hidden');
				}
				$buttons_div.find('span').text((current_page + 1) + ' of ' + num_pages);
			}
			
			function show_buttons() {
				var $div = $('<div></div>');
				var buttons = {'prev': '&larr; prev', 'page': 'page', 'next': 'next &rarr;'};
				if (num_pages == 1) {
					buttons = {};
				}
				else {
					$div.append('<br />');
				}
				for (var i in buttons) {
					if (buttons[i] == 'page') {
						var text = (current_page + 1) + ' of ' + num_pages;
						var $button = $('<span>' + text + '</span>');
					}
					else {
						var text = buttons[i];
						var $button = $('<a>' + text + '</a>');
						$button.hover(function() {
							$(this).css({'color': 'black', 'background': 'white'});
						}, function() {
							$(this).css({'color': 'white', 'background':  'black'});
						}).css({
							'cursor': 'pointer'
						});
					}
					$button.addClass(i);
					if (i == 'prev') {
						$button.addClass('hidden').css({'visibility': 'hidden', 'display': 'inline'});
						$button.click(function() {
							if (image_open) {
								close_image(prev_page);
							}
							else {
								prev_page();
							}
							return false;
						});
					}
					else if (i == 'next') {
						if (num_pages == 1) $button.addClass('hidden').css({'visibility': 'hidden', 'display': 'inline'});
						$button.click(function() {
							if (image_open) {
								close_image(next_page);
							}
							else {
								next_page();
							}
							return false;
						});
					}
					$button.css({
						'padding': '5px',
						'background': '#000',
						'color': 'white',
						'margin': '5px',
						'zoom': '1'
					});
					$div.append($button);
				}
				$div.css({
					'position': opts.elements_position_type,
					'top': (opts.elements_position_type == 'absolute' ? $(document).scrollTop() : 0) + $(window).height() - 80,
					'left': $(document).width() / 2,
					'margin-left': '-200px',
					'width': '400px',
					'text-align': 'center',
					'line-height': '35px',
					'z-index': Z_BUTTONS
				});
				if (opts.link) {
					var $view_all = $('<a>').css({
						'padding': '5px',
						'background': '#000',
						'color': 'white',
						'margin': '5px',
						'cursor': 'pointer'
					}).text('View All').click(function() {
						location.href = opts.link;
					}).hover(function() {
						$(this).css({'color': 'black', 'background': 'white'});
					}, function() {
						$(this).css({'color': 'white', 'background':  'black'});
					});
					$div.prepend($view_all);
				}
				$outer_container.append($div);
				$div.addClass('kastack-buttons');
				$div.fadeIn();
				$buttons_div = $div;
			}
			
			function hide_current_page() {
				if ($current_page_container) {
					$current_page_container.fadeOut();
				}
			}
			
			function show_page(page_id, animation_type) {
				var page = pages[page_id];
				switch (animation_type) {
					case 'unstack':
						animate_page_unstack(page);
						break;
					case 'scroll_left':
						animate_page_scroll_left(page);
						break;
					case 'scroll_right':
						animate_page_scroll_right(page);
						break;
				}
			}
			
			function get_maximum_image_prop(p) {
				var n = 0;
				for (var i = 0; i < pages.length; i++) {
					for (var j = 0; j < pages[i].length; j++) {
						var prop = pages[i][j][p];
						if (prop > n) n = prop;						
					}
				}
				return n;
			}
			
			function animate_page_scroll_left(page) {
				var width = ((opts.width + opts.padding * 2) * opts.per_row + opts.margin * (opts.per_row - 1)) / 2;
				
				var $div = $('<div></div>');
				var page_centre_x = page_centre_x_c;
				var page_centre_y = page_centre_y_c;
				var max_width = opts.width;
				var max_height = opts.height;
				
				var images_area_width = (max_width + opts.padding * 2) * opts.per_row + opts.margin * (opts.per_row - 1);
				var images_area_height = (max_height + opts.padding * 2) * (opts.per_page / opts.per_row) + opts.margin * (opts.per_page / opts.per_row - 1);
				var area_x = page_centre_x - images_area_width / 2 + opts.padding;
				var area_y = page_centre_y - images_area_height / 2 + (opts.elements_position_type == 'absolute' ? $(document).scrollTop() : 0);
				var current_x = 0;
				var current_y = area_y;
				
				for (var i = 0; i < page.length; i++) {
					$div.append(page[i].element);
					page[i].element.click(function() { if (image_open) return false; open_image($(this)); return false; });

					
					page[i].element.css({
						'position':	opts.elements_position_type,
						'visibility': 'visible',
						'display':	'block',
						'opacity':	1,
						'width':	opts.width,
						'height':	opts.height,
						'top':		current_y,
						'left':		$(document).width() + current_x,
						'z-index':	i ? Z_LIST_IMAGES : Z_LIST_IMAGES_TOP
					});
					page[i].element.find('img').css({
						'width':	opts.width,
						'height':	opts.height
					});
					current_x += max_width + opts.margin;
					
					if (!((i - 1) % opts.per_row ) && i) {
						current_x = 0;
						current_y += max_height + opts.margin;
					}
				}
				
				$current_page_container.find('a').animate({
					'left': '-=' + (images_area_width + ($(document).width() - images_area_width) / 2 - opts.padding) + 'px'
				}, speed(1));
				setTimeout(function() {
					$outer_container.append($div);
					$div.css({
						'position': 'relative',
						'left': '0px'
					});
					$div.find('a').animate({
						'left': '-=' + (images_area_width + ($(document).width() - images_area_width) / 2 - opts.padding) + 'px'
					}, speed(1));
					$current_page_container = $div;
				}, speed(0.1));
				
			}
			
			function animate_page_scroll_right(page) {
				var width = ((opts.width + opts.padding * 2) * opts.per_row + opts.margin * (opts.per_row - 1)) / 2;
				
				var $div = $('<div></div>');
				var page_centre_x = page_centre_x_c;
				var page_centre_y = page_centre_y_c;
				var max_width = opts.width;
				var max_height = opts.height;

				var images_area_width = (max_width + opts.padding * 2) * opts.per_row + opts.margin * (opts.per_row - 1);
				var images_area_height = (max_height + opts.padding * 2) * (opts.per_page / opts.per_row) + opts.margin * (opts.per_page / opts.per_row - 1);
				var area_x = page_centre_x - images_area_width / 2 + opts.padding;
				var area_y = page_centre_y - images_area_height / 2 + (opts.elements_position_type == 'absolute' ? $(document).scrollTop() : 0);
				var current_x = 0;
				var current_y = area_y;
				
				for (var i = 0; i < page.length; i++) {
					$div.append(page[i].element);
					page[i].element.click(function() { if (image_open) return false; open_image($(this)); return false; });

					page[i].element.css({
						'position':	opts.elements_position_type,
						'visibility': 'visible',
						'display':	'block',
						'opacity':	1,
						'width':	opts.width,
						'height':	opts.height,
						'top':		current_y,
						'left':		-images_area_width + opts.padding * 2 + current_x,
						'z-index':	i ? Z_LIST_IMAGES : Z_LIST_IMAGES_TOP
					});
					page[i].element.find('img').css({
						'width':	opts.width,
						'height':	opts.height
					});
					current_x += max_width + opts.margin;
					
					if (!((i - 1) % opts.per_row ) && i) {
						current_x = 0;
						current_y += max_height + opts.margin;
					}
				}
				
				$current_page_container.find('a').animate({
					'left': '+=' + (images_area_width + ($(document).width() - images_area_width) / 2) + 'px'
				}, speed(1), function() { $(this).remove(); });
				setTimeout(function() {
					$outer_container.append($div);
					$div.css({
						'position': 'relative',
						'left': '0px'
					});
					$div.find('a').animate({
						'left': '+=' + (images_area_width + ($(document).width() - images_area_width) / 2 - opts.padding) + 'px'
					}, speed(1));
					$current_page_container = $div;
				}, speed(0.1));
				
			}
			
			function animate_page_unstack(page) {
				var stack_position = $ul.offset();

				var sw = $ul.width();
				var sh = $ul.height();

				var max_width = opts.width;
				var max_height = opts.height;

				var starting_x = stack_position.left + (sw / 2) - (max_width / 2);
				var starting_y = stack_position.top - $(document).scrollTop() + (sh / 2) ;
				var $div = $('<div></div>');
				var page_centre_x = $(window).width() / 2;
				var page_centre_y = $('body').height() / 2;
				page_centre_x_c = page_centre_x;
				page_centre_y_c = page_centre_y;

				var images_area_width = (max_width + opts.padding * 2) * opts.per_row + opts.margin * (opts.per_row - 1);
				var images_area_height = (max_height + opts.padding * 2) * (opts.per_page / opts.per_row) + opts.margin * (opts.per_page / opts.per_row - 1);
				var area_x = page_centre_x - images_area_width / 2 + opts.padding;
				var area_y = page_centre_y - images_area_height / 2;
				var current_x = area_x;
				var current_y = area_y;
				

				
				for (var i = 0; i < page.length; i++) {
					$div.append(page[i].element);
					page[i].element.click(function() { if (image_open) return false; open_image($(this)); return false; });
					page[i].element.css({
						'position':		opts.elements_position_type,
						'visibility':	'visible',
						'display':		'block',
						'opacity':		1,
						'width':		opts.width,
						'height':		opts.height,
						'top':			starting_y,
						'left':			starting_x,
						'z-index':		i ? Z_LIST_IMAGES : Z_LIST_IMAGES_TOP
					});
					page[i].element.find('img').css({
						'width':	opts.width,
						'height':	opts.height
					});

					page[i].element.animate({
						'top': current_y,
						'left': current_x 
					}, speed(1.2 - Math.random() * 0.4));
					
					current_x += max_width + opts.margin;
					
					if (!((i - 1) % opts.per_row ) && i) {
						current_x = area_x;
						current_y += max_height + opts.margin;
					}
				}
				
				$outer_container.append($div);
				$div.css({
					'position': 'relative'
				})
				$div.show();
				$current_page_container = $div;
			}
			
			function open_image($img) {
				if ($buttons_div) $buttons_div.fadeOut();
				$img.stop(true, true);
				image_open = true;
				var $original_img = $img.clone();
				$open_image_element = $img;
				$original_image_element = $original_img;

				
				var large_height = opts.large_height;
				var large_width = opts.large_width;
				
				if (large_height + opts.padding * 2 > $(window).height() - 150) {
					var max_height = $(window).height() - 150;
					var factor = large_height / max_height;
					var large_width = large_width / factor;
					var large_height = max_height;
				}
				
				$img.addClass('opening');
				$outer_container.find('a:not(.opening,.kastack-buttons *)').animate({
					'opacity': 0
				}, speed(0.5));
				$img.removeClass('opening');
				var position = $img.offset();
				var centre_x = $(window).width() / 2;
				var centre_y = $(window).height() / 2;

				var $loading = $('<img />').attr('src', '/images/ajax-loader.gif').css({
					'z-index': Z_LOADING_IMAGE,
					'position': opts.elements_position_type,
					'top': centre_y - 32,
					'left': centre_x - 32
				}).fadeIn(speed(0.5));
				$('body').append($loading);
				var $large_image = $('<img />').load(function() {
					$loading.fadeOut(speed(0.5), function() { $(this).remove(); });
					$img.find('img').attr('src', $img.attr('href'));
					$img.css({
						'z-index': Z_LARGE_IMAGE,
						'position': opts.elements_position_type
					});
					$img.animate({
						'top': centre_y - large_height / 2 - opts.padding * 2,
						'left': centre_x - large_width / 2 - opts.padding,
						'width': large_width,
						'height': large_height
					}, speed(0.5));
					$img.find('img').animate({
						'width': large_width,
						'height': large_height
					}, speed(0.5), function() {
						
						var $close = $('<img />').attr('src', '/images/fancy_close.png').css({
							'z-index': Z_CLOSE_BUTTON,
							'position': opts.elements_position_type,
							'top': centre_y - large_height / 2 - opts.padding * 2 - 10, // ???
							'left': centre_x + large_width / 2 - opts.padding * 2
						});
						$close_button = $close;
						$close.fadeIn(speed(0.5));
						$('body').append($close);
						$close.click(function() {
							close_image();
						})
					});
				}).attr('src', $img.attr('href'));
			}
		
			function close_image(after) {
				if ($buttons_div) $buttons_div.fadeIn();		
				$open_image_element.fadeOut(speed(0.5), function() {
					if ($original_image_element) {
						$open_image_element.find('img').attr('src', $original_image_element.find('img').attr('src'));
					}
					$open_image_element.remove();
					$open_image_element = $original_image_element = $close_button = null;
					image_open = false;
					if (after) after();
				});
				if ($current_page_container) {
					$current_page_container.append($original_image_element);
					$original_image_element.click(function() { open_image($(this)); return false; });
				}
				else {
					$darken.fadeOut(speed(1), function() { $(this).remove(); });
				}
				if ($outer_container) {
					$outer_container.find('a').animate({
						opacity: 1
					}, speed(1));
				}
				$close_button.fadeOut(speed(0.5), function() { $(this).remove(); });
				
			
			}
		
			function open_single_image($img) {
				darken();
				$outer_container = $('<div></div>');
				$outer_container.click(function() {
					close_image();
					$outer_container.remove();
					return false;
				})
				$outer_container.css({
					'position': opts.elements_position_type,
					'z-index': Z_OUTER_CONTAINER,
					'top': 0,
					'left': 0,
					'width': $(document).width(),
					'height': $(document).height()
				});
				$('body').append($outer_container);
				image_open = true;
				var position = $img.offset();
				var $copy = $img.clone();
				$open_image_element = $copy;
				$copy.css({
					'z-index': Z_LIST_IMAGES_TOP,
					'left': position.left, // why??
					'top': position.top - (opts.elements_position_type == 'fixed' ? $(document).scrollTop() : 0),
					'position': opts.elements_position_type,
					'padding': opts.padding,
					'background': 'white',
					'width': opts.width,
					'height': opts.height
				});
				$copy.find('img').css({
					'display': 'block',
					'width': opts.width,
					'height': opts.height
				})
				$('body').append($copy);

				var centre_x = $(document).width() / 2;
				var centre_y = $(window).height() / 2 + (opts.elements_position_type == 'absolute' ? $(document).scrollTop() : 0);

				var $loading = $('<img />').attr('src', '/images/ajax-loader.gif').css({
					'z-index': Z_LOADING_IMAGE,
					'position': opts.elements_position_type,
					'top': centre_y - 32,
					'left': centre_x - 32
				}).fadeIn(speed(0.5));
				$('body').append($loading);
				var $large_image = $('<img />').load(function() {
					$loading.fadeOut(speed(0.5), function() { $(this).remove(); });
					$copy.find('img').attr('src', $copy.attr('href'));
					$copy.css({
						'z-index': Z_LARGE_IMAGE,
						'position': opts.elements_position_type,
						'width': opts.width,
						'height': opts.height
					});
					$copy.find('img').css({
						'width': opts.width,
						'height': opts.height
					})
					$copy.animate({
						'top': centre_y - opts.large_height / 2 - opts.padding * 2,
						'left': centre_x - opts.large_width / 2 - opts.padding,
						'width': opts.large_width,
						'height': opts.large_height
					}, speed(0.5));
					$copy.find('img').animate({
						'width': opts.large_width,
						'height': opts.large_height
					}, speed(0.5), function() {
						
						var $close = $('<img />').attr('src', '/images/fancy_close.png').css({
							'z-index': Z_CLOSE_BUTTON,
							'position': opts.elements_position_type,
							'top': centre_y - opts.large_height / 2 - opts.padding * 2 - 10,
							'left': centre_x + opts.large_width / 2 - opts.padding * 2
						});
						$close_button = $close;
						$close.fadeIn(speed(0.5));
						$('body').append($close);
						$close.click(function() {
							close_image();
							$outer_container.remove();
							return false;
						})
					});
				}).attr('src', $img.attr('href'));
			}
			
		});
	}
})(jQuery);
