(function()
{
	var self = $();
	
	Lotus = function(options)
	{
		var defaults = {
		};
		
		self = this;
		self.$options = $.extend(defaults, options);

		//The direction we're going indicates which page to load next
		self.$direction = 0;
		
		// When an ad comes up, we don't want them to be able
		// to skip it immediately
		self.$lockSelection = false;
	
		self.$initData = function()
		{
			var state = self.getSyncState();
			
			self.$activeThumb = self.getThumbByIdentifier(state.image); 
			self.$UI.$thumbMargin = parseInt( self.$activeThumb.css('margin-right') );
			self.$UI.$thumbWidth = self.$activeThumb.outerWidth();
			self.$UI.$groupWidth = (self.$limit * (self.$UI.$thumbWidth + self.$UI.$thumbMargin));
			
			self.selectImage( self.$activeThumb );
		}
	}
	
	Lotus.prototype.initialize = function(navigation)
	{
		// UI Controller
		self.$UI = new LotusUI();

		// Navigation handler
		self.$navigation = new LotusNavigation();
		
		// Set initial state
		self.resetState();
		self.$UI.initialize(self);
		self.$navigation.initialize(self);		
	}
	
	Lotus.prototype.getLocalState = function()
	{
		var localState = {
			image: self.$activeImage.length ? self.$activeImage.attr('base') : '#',
			page: self.$activePage,
			hash: '#'
		};
		
		localState.hash = self.$navigation.getLocation(localState);
		
		return localState;
	}
	
	Lotus.prototype.getSyncState = function()
	{
		var localState = self.getLocalState();
		var state = self.$navigation.getState();
		
		if(state.image != localState.image)
		{
			localState.image = state.image;
		}
		
		if(state.page != localState.page)
		{
			localState.page = state.page;
		}
		
		return localState;
	}
	
	Lotus.prototype.loadState = function(state)
	{
		// Get us back to original state
		self.resetState();
		//Get our initial data
		self.getResults(state, self.$initData);
	}
	
	Lotus.prototype.resetState = function()
	{
		self.$UI.$imageList.remove('div.image');
		self.$UI.$thumbList.remove('div.thumb');

		self.$activePage = 1;
		self.$page = 1;
		self.$pages = 1;
		self.$pageBuffer = 2; //Number of pages to keep before GC
		self.$totalResults = 0;
		self.$limit = 1;
		self.$pageBeingRequested = false;
		
		self.$activeImage = self.$UI.$imageList.find('div.image:first-child');
		self.$activeThumb = self.$UI.$thumbList.find('div.thumb:first-child');
	}

	Lotus.prototype.getNextThumb = function(t)
	{
		var nextInGroup = t.next();
		var nextGroup = t.parent().next();
		
		if(nextInGroup.attr('id'))
		{
			return nextInGroup;
		}
		else
		{
			return nextGroup.find('div.thumb:first-child');
		}
		
		return t;
	}
	
	Lotus.prototype.getPreviousThumb = function(t)
	{
		var previousInGroup = t.prev();
		var previousGroup = t.parent().prev();
		
		if(previousInGroup.attr('id'))
		{
			return previousInGroup;
		}
		else if(previousGroup.attr('id'))
		{
			return previousGroup.find('div.thumb:last-child');
		}
		
		return t;
	}
	
	Lotus.prototype.getNextImage = function(i)
	{
		var nextInGroup = i.next();
		var nextGroup = i.parent().next();
		
		if(nextInGroup.attr('id'))
		{
			return nextInGroup;
		}
		else if(nextGroup.attr('id'))
		{
			return nextGroup.find('div.image:last-child');
		}
		return i;
	}
	
	Lotus.prototype.getPreviousImage = function(i)
	{
		var previousInGroup = i.prev();
		var previousGroup = i.parent().prev();
		
		if(previousInGroup.attr('id'))
		{
			return previousInGroup;
		}
		else if(previousGroup.attr('id'))
		{
			return previousGroup.find('div.image:last-child');
		}
		return i;
	}
	
	//Find the *absolute* index of a thumbnail across multiple groups.
	Lotus.prototype.getAbsoluteIndex = function(t)
	{
		var thumbGroup = t.parent();
		var groupPage = parseInt( thumbGroup.attr('page') );
		var modifier = 0;
		
		if(groupPage > self.$pageBuffer)
		{
			var firstAvailableGroup = self.getFirstAvailableGroup().thumbGroup;
			var firstAvailablePage = parseInt( firstAvailableGroup.attr('page') );
			
			modifier = ((self.$limit * firstAvailablePage) - self.$limit);
		}
		
		//Relative to the amount of groups that haven't
		//Been garbage collected
		var relativeIndex = self.$UI.$thumbList.find('div.thumb').index(t);
		var absoluteIndex = relativeIndex + modifier;
		
		return absoluteIndex;
	}
	
	Lotus.prototype.getThumbByIdentifier = function(base)
	{
		var thumb = $('#t_' + base);
		
		if(thumb.attr('id'))
		{
			return thumb;
		}
		
		return self.$UI.$thumbList.find('div.thumb:first-child');
	}
	
	Lotus.prototype.getThumbByImage = function(image)
	{
		var base = image.attr('base');
		
		if(base)
		{
			return self.getThumbByIdentifier(base);
		}
		
		return $();
	}
	
	Lotus.prototype.getFirstAvailableGroup = function(contextPage)
	{
		var thumbGroup = self.$UI.$thumbList.find('div.group:first-child');
		var imageGroup = $();
		
		if(contextPage)
		{
			var contextGroup = $('#t_page_' + contextPage);
			if(contextGroup.attr('id'))
			{
				var previousGroup = contextGroup.prev();
				if(previousGroup.attr('id'))
				{
					thumbGroup = previousGroup;
				}
				thumbGroup = contextGroup;
			}
		}
		
		imageGroup = $('#i_page_' + thumbGroup.attr('page'));
		
		return {
			thumbGroup: thumbGroup,
			imageGroup: imageGroup
		};
	}
	
	Lotus.prototype.getLastAvailableGroup = function(contextPage)
	{
		var thumbGroup = $('#t_page_' + self.$activePage);
		var imageGroup = $();
		
		if(contextPage)
		{
			var contextGroup = $('#t_page_' + contextPage);
			if(contextGroup.attr('id'))
			{
				var nextGroup = contextGroup.next();
				if(nextGroup.attr('id'))
				{
					thumbGroup = nextGroup;
				}
				thumbGroup = contextGroup;
			}
		}
		
		imageGroup = $('#i_page_' + thumbGroup.attr('page'));
		
		return {
			thumbGroup: thumbGroup,
			imageGroup: imageGroup
		};
	}
	
	Lotus.prototype.getImagePage = function(image)
	{
		return parseInt(image.parent().attr('page'));
	}
	
	Lotus.prototype.nextImage = function(e)
	{
		if(!(self.$direction > -1 && self.$pageBeingRequested != false))
		{
			var nextThumb = self.getNextThumb(self.$activeThumb);
			
			if(nextThumb.attr('id'))
			{
				self.$direction = 1;
				self.selectImage(nextThumb);
			}
			self.$UI.highlightArrow(self.$direction);
		}
	}
	
	Lotus.prototype.previousImage = function()
	{
		//console.log(self.$direction, self.$pageBeingRequested);
		if(!(self.$direction < 1 && self.$pageBeingRequested != false))
		{
			var prevThumb = self.getPreviousThumb(self.$activeThumb);
			
			if(prevThumb.attr('id'))
			{
				self.$direction = -1;
				self.selectImage(prevThumb);
			}
			self.$UI.highlightArrow(self.$direction);
		}
	}
	
	Lotus.prototype.thumbClick = function()
	{
		var thumb = $(this);
		var index = self.getAbsoluteIndex(thumb);
		var page = thumb.parent().attr('page');
		var activePage = self.$activePage;
		var activeIndex = self.$activeThumb.index();
		
		//self.is needed to determine the page "direction"
		//Since thumbs are split into groups, we have to compare
		//Page as well as thumb index
		
		if(page > activePage) self.$direction = 1;
		if(page < activePage) self.$direction = -1;
		if(page == activePage && index > activeIndex) self.$direction = 1;
		if(page == activePage && index < activeIndex) self.$direction = -1;
		if(page == activePage && index == activeIndex) return; //Do nothing
		
		self.selectImage(thumb);
	}
	
	Lotus.prototype.selectImage = function(thumb)
	{
		if(self.$lockSelection) return;
		
		var thumb = thumb;
		var image = $('#img_' + thumb.attr('base'));
		
		if(thumb.length && image.length)
		{
			image
				.removeClass('next')
				.removeClass('prev');
			
			if(self.$activeThumb && self.$activeImage)
			{
				self.$activeThumb.removeClass('active');
				self.$activeThumb.addClass('inactive');
				self.$activeImage.removeClass('active');
			}
			
			self.$activeThumb = thumb;
			self.$activeImage = image;
			
			self.$activeThumb.addClass('active');
			self.$activeThumb.removeClass('inactive');
			self.$activeImage.addClass('active');
			
			if(image.hasClass('adUnit'))
			{
				self.$UI.$shareUtil.hide();
				self.showAdUnit(image);
			}
			
			self.$UI.$imageList.find('div.next, div.prev').css({
				zIndex: 0
			});
			
			// Update active page
			self.$activePage = self.getImagePage( image );
			// Preload the image
			self.preloadImages();
			// Position primary & secondary images
			self.updateImageState();	
			// Handle left/right arrows
			self.$UI.checkArrows();
			// Keep thumbnails in the right place
			self.$UI.alignThumbnails();
			
			// Keep navigation updated
			self.$navigation.setSelectedImage(self.$activeImage);
			self.$navigation.setCurrentPage(
				self.getImagePage( self.$activeImage )
			);
			
			// Update location
			self.$navigation.updateLocation();
		}
	}
	
	Lotus.prototype.updateImageState = function()
	{
		var prev = self.getPreviousImage(self.$activeImage);
		var next = self.getNextImage(self.$activeImage);
		
		self.$UI.$imageList.find('div.image')
			.removeClass('prev')
			.removeClass('next');
		
		self.$UI.$imageList
			.find('div.next, div.prev')
			.stop(true, true);
		
		self.$UI.$imageList
			.find('div.adUnit iframe')
			.remove();
		
		if(self.$activeImage.attr('id'))
		{
			var dimensions = self.getImagePosition(self.$activeImage, 0);
			
			self.$activeImage
			.removeClass('gs')
			.css({
				top: dimensions.top,
				left: dimensions.left,
				width: dimensions.width,
				height: dimensions.height,
				zIndex: 15,
				opacity: 1
			});
				
			self.$activeImage
				.unbind('click', self.nextImage)
				.unbind('click', self.previousImage);
				
			self.$activeThumb.removeClass('gs');
		}
		
		if(prev != self.$activeImage && prev.attr('id'))
		{
			var dimensions = self.getImagePosition(prev, -1);
			var thumb = self.getThumbByImage(prev);
			
			prev
				.addClass('prev gs')
				.css({
					opacity: self.$direction > -1 ? .3 : 0,
					width: dimensions.width,
					height: dimensions.height,
					top: dimensions.top,
					left: dimensions.left,
					zIndex: 10,
					opacity: .3
				});
				
			thumb.addClass('gs');
			
			prev.click(self.previousImage);
		}
		
		if(next != self.$activeImage && next.attr('id'))
		{
			var dimensions = self.getImagePosition(next, 1);
			var thumb = self.getThumbByImage(next);
			
			next
				.addClass('next gs')
				.css({
					opacity: self.$direction > -1 ? 0 : .3,
					width: dimensions.width,
					height: dimensions.height,
					top: dimensions.top,
					left: dimensions.left,
					zIndex: 10,
					opacity: .3
				});
			
			thumb.addClass('gs');
			
			next.click(self.nextImage);
		}
	}
	
	Lotus.prototype.getImagePosition = function(image, direction)
	{
		var active = direction == 0 ? true : false;
		var width = parseInt( image.attr( active ? 'primaryWidth' : 'secondaryWidth' ) );
		var height = parseInt( image.attr( active ? 'primaryHeight' : 'secondaryHeight' ) );
		var offsetTop = image.offset().top;
		var offsetLeft = image.offset().left;
		var centerX = self.$UI.$imageList.outerWidth() / 2;
		var centerY = self.$UI.$imageList.outerHeight() / 2;
		var top = centerY - (height / 2);
		var left = centerX - (width / 2);
		var shareUtilHeight = image.find('.share_util').outerHeight();
		
		top -= (self.$UI.$header.outerHeight() / 2);
		top = Math.max(top, self.$UI.$header.outerHeight() + 10);
		
		if(direction < 0)
		{
			left = (self.$UI.$documentWidth * .35) - (width / 2);
		}
		
		if(direction > 0)
		{
			left = (self.$UI.$documentWidth * .65) - (width / 2);
		}
		
		return {
			width: width + 'px',
			height: height + 'px',
			top: top + 'px',
			left: left + 'px'
		};
	}
	
	// Preloading images
	Lotus.prototype.preloadImages = function()
	{
		var activeGroup = self.$activeThumb.parent();
		var activePage = parseInt( activeGroup.attr('page') );
		var absoluteIndex = self.getAbsoluteIndex(self.$activeThumb);
		var firstAvailableGroup = self.getFirstAvailableGroup().thumbGroup;
		var firstAvailablePage = parseInt( firstAvailableGroup.attr('page') );
		var lastAvailableGroup = self.getLastAvailableGroup().thumbGroup;
		var lastAvailablePage = parseInt( lastAvailableGroup.attr('page') );
		var params = self.getSyncState();
		
		// When we initially load, we have no base direction. 
		// Try and load preceeding and following pages.
		var override = self.$direction == 0 ? true : false;
		
		// Handle lazy loading of new images
		// When going backwards, we have to calculate
		// where our trigger point is
		// However, if our current page isn't even greater
		// than the page buffer, don't bother.
		if(activePage > self.$pageBuffer && firstAvailablePage > 1)
		{
			//Need to figure out what point going backwards should trigger
			//Image loading.
			var triggerIndex = Math.max(0, (self.$limit * firstAvailablePage));
			
			//console.log("BACKWARDS: activePage: " + activePage);
			//console.log("BACKWARDS: triggerIndex: " + triggerIndex + " absoluteIndex: " + absoluteIndex + " self.$limit: " + self.$limit + " div: " + (absoluteIndex - (self.$limit / 2)));
			if(absoluteIndex - ((self.$limit * self.$pageBuffer) / 2) <= triggerIndex && self.$pageBeingRequested == false)
			{
				params.image = null;
				params.page = firstAvailablePage - 1;
				// Run preloader again once we finish, so that we don't call them both at once
				// And avoid having any conflicts
				self.getResults(params, self.alignThumbnails);
			}
		}
		
		if(lastAvailablePage <= self.$pages)
		{
			var triggerIndex = absoluteIndex + ((self.$limit * self.$pageBuffer));
			
			if(triggerIndex >= self.$totalResults && self.$pageBeingRequested == false)
			{
				params.image = null;
				params.page = lastAvailablePage + 1;
				
				self.getResults(params, self.alignThumbnails);
			}
		}
		
		// Clean up existing images so we don't become a memory hog
		if(activePage > Math.max(1, firstAvailablePage + self.$pageBuffer))
		{
			//Forward, cleaning up behind us
			//console.log("FORWARD: firstAvailablePage: " + firstAvailablePage);
			//console.log("FORWARD: activePage: " + activePage + " > firstAvailable + pageBuffer: " + (firstAvailablePage + self.$pageBuffer));
			self.garbageCollection();
		}
		if(activePage < Math.min(lastAvailablePage - self.$pageBuffer, self.$pages))
		{
			//Backwards, cleaning up in fornt of us
			//console.log("BACKWARDS: lastAvailablePage: " + lastAvailablePage);
			//console.log("BACKWARDS: activePage: " + activePage + " < lastAvailable - pageBuffer: " + (lastAvailablePage - self.$pageBuffer));
			self.garbageCollection();
		}
	}
	
	//Since a user might page through hundreds or thousands of images
	//We don't want to keep the images offscreen (outside a buffer)
	//In the browser's memory. It would only make the interface
	//respond slower and hog resources.
	Lotus.prototype.garbageCollection = function()
	{
		var activeGroup = self.$activeThumb.parent();
		var activePage = parseInt( activeGroup.attr('page') );
		var list = false;
		
		//console.log('Garbage collection initialize');
		
		if(self.$direction > 0)
		{
			list = self.$UI.$thumbList.find('div.group:lt(' + activeGroup.index() + ')');
		}
		else if(self.$direction < 0)
		{
			list = self.$UI.$thumbList.find('div.group:gt(' + activeGroup.index() + ')');
		}
		
		if(list)
		{
			list.each(function()
			{
				var page = parseInt( $(this).attr('page') );
				if(self.$direction > 0)
				{
					//console.log('page: ' + page + ' activePage: ' + activePage + ' self.$pageBuffer: ' + self.$pageBuffer);
					if(page < activePage - self.$pageBuffer)
					{
						//console.log('Removed page ' + page);
						$('#i_page_' + page).remove();
						$(this).remove();
					}
				}
				else if(self.$direction < 0)
				{
					//console.log('Removed page ' + page);
					if(page > activePage + self.$pageBuffer)
					{
						$('#i_page_' + page).remove();
						$(this).remove();
					}
				}
			});
		}
	}
	
	Lotus.prototype.loadImage = function(thumb)
	{
		if(thumb.hasClass('unloaded'))
		{
			var image = $('#img_' + thumb.attr('base'));
			
			image.find('div.ibg').css({ background: 'url(' + image.attr('gs') + ') 50% 50% no-repeat' });
			thumb.find('div.tbg').css({ background: 'url(thumb.php?p=' + thumb.attr('thumb_gs') + ') 50% 50% no-repeat' });
			thumb.removeClass('unloaded');
		}
	}
	
	// Ajax loading images
	Lotus.prototype.getResults = function(params, cb)
	{
		//Make sure we haven't already somehow loaded self.page
		if(!self.$UI.$thumbList.find('#t_page_' + params.page).attr('id'))
		{
			self.$pageBeingRequested = params.page;
			$.getJSON('media.getresults.ajax', params, function(results)
			{
				self.processResults(results, cb);
			});
		}
	}
	
	Lotus.prototype.processResults = function(response, cb)
	{	
		var imageGroup = $('<div></div')
			.addClass('group')
			.attr('page', response.data.page)
			.attr('id', 'i_page_' + response.data.page);
		
		var thumbGroup = $('<div></div>')
			.addClass('group')
			.attr('page', response.data.page)
			.attr('id', 't_page_' + response.data.page);
		
		var groupToInsertBefore = self.getFirstAvailableGroup(self.$page);
		var groupToInsertAfter = self.getLastAvailableGroup(self.$page);
		
		//Determine where to insert the group
		if(!groupToInsertBefore.thumbGroup.attr('id') || !groupToInsertAfter.thumbGroup.attr('id'))
		{
			self.$UI.$imageList.append(imageGroup);
			self.$UI.$thumbList.append(thumbGroup);
		}
		//Determine if we insert before or after, based on direction
		else if(response.data.page > self.$activePage)
		{
			//groupToInsertAfter.after(group);
			imageGroup.insertAfter(groupToInsertAfter.imageGroup);
			thumbGroup.insertAfter(groupToInsertAfter.thumbGroup);
		}
		else
		{
			imageGroup.insertBefore(groupToInsertBefore.imageGroup);
			thumbGroup.insertBefore(groupToInsertBefore.thumbGroup);
		}
		
		for(var i = 0; i < response.data.results.length; i++)
		{
			var image = self.$UI.$imageTemplate.clone();
			var thumb = self.$UI.$thumbTemplate.clone();
			var row = response.data.results[i];
			
			image
				.attr('id', row.id)
				.attr('local', row.url)
				.attr('img', row.perm)
				.attr('gs', row.gs)
				.attr('base', row.basename)
				.attr('primaryWidth', row.width)
				.attr('primaryHeight', row.height)
				.attr('secondaryWidth', row.secondaryWidth)
				.attr('secondaryHeight', row.secondaryHeight)
				.addClass(row.img_class)
				.addClass('gs')
				.css({
					width: '0px',
					height: '0px'
				});
			
			image
				.find('div.ibg')
				.css({
					background: 'url(' + row.background +') 50% 50% no-repeat'
				});
				
			image
				.find('div.gbg')
				.css({
					background: 'url(' + row.gs +') 50% 50% no-repeat'
				});
			
			image
				.find('div.ibg .txt')
				.val(row.perm);
			
			thumb
				.attr('id', row.thumb_id)
				.attr('base', row.basename)
				.attr('thumb_url', row.thumb_url)
				.attr('thumb_gs', row.thumb_gs)
				.addClass(row.thumb_class)
				.addClass('gs');
			
			thumb
				.find('div.tbg')
				.css({
					background: 'url(' + row.thumb_url + ') 50% 50% no-repeat'
				});
				
			thumb
				.find('div.gbg')
				.css({
					background: 'url(' + row.thumb_gs + ') 50% 50% no-repeat'
				});
			
			thumb.click(self.thumbClick);
			thumb.hover(self.thumbMouseOver, self.thumbMouseOut);
			
			// Thumbnail pages are stored in groups for easy cleanup
			imageGroup.append(image);
			thumbGroup.append(thumb);
			
			if(row.type == 'ad')
			{
				self.showAdUnit(image);
			}
		}
		
		self.$totalResults += i;
		self.$page = response.data.page;
		self.$pages = response.data.pages;
		self.$limit = response.data.limit;
	
		self.$pageBeingRequested = false;
		
		if(cb && i > 0)
		{
			cb.call();
		}
	}

})();
