(function($){
	$.emrlGallery = function(el, options){
		var base = this;
		
		// Gallery container, usually a <ul>
		base.container = $(el);
		
		// Collection of slides, usually <li>s
		base.slides = base.container.children();
		
		// Whether or not the gallery is in the middle of a transition between slides
		base.transitioning = false;
		
		// Only initiate once
		if (base.container.data('emrlGallery')) {
			return;
		}
		
		 // Add a reverse reference to the DOM object
		base.container.data('emrlGallery', base);
		
		/**
		 * Initiation, this starts it all
		 */
		base.init = function(){
			// Extend the options
			base.options = $.extend({},$.emrlGallery.defaultOptions, options);
			
			// Build the HTML structure
			base.build();
			
			// Wait until last image is done loading
			base.slides.last().find('img:first').load(function() {
				base.slidesWrap.addClass('loaded');
				base.filmstripContainer.show();
				
				// Get the filmstrip dimensions
				base.filmstripDimensions();
				
				// Get each slide's dimensions
				base.slideDimensions();
				
				// Enable autoplay
				if (base.options.interval > 0)
					base.autoplay = true;
				
				// Start the gallery
				base.showSlide(base.options.startSlide);
			});
		};
		
		/**
		 * Build the HTML structure for the gallery
		 */
		base.build = function() {
			/*
			wrap
				slideWrap
					container
						slide
						slide
						...
					/container
				/slideWrap
				filmstripWrap
					filmstrip
						li
							img
						/li
						...
					/filmstrip
				/filmstripWrap
			/wrap
			*/
			base.container.addClass(base.options.containerClass);
			
			// Wrap the entire gallery with a container
			var wrap = $('<div />', {
				'class' : base.options.wrapClass
			});
			base.container.wrap(wrap);
			base.wrap = base.container.parent();
			
			// Wrap the gallery again
			var slidesWrap = $('<div />', {
				'class' : base.options.slidesWrapClass
			});
			base.container.wrap(slidesWrap);
			base.slidesWrap = base.container.parent();
			
			// Create filmstrip navigation
			base.filmstrip = $('<ul />', {
				'class' : base.options.filmstripClass
			}).appendTo(base.wrap);
			
			base.slides.each(function(i, li) {
				var li = $(li);
				var img = li.find('img:first');
				
				img.attr('src', img.data('src'));
				
				// Create titles if there isn't one already
				if ( ! li.find('div.' + base.options.titleClass)[0]) {
					var title = img.attr('title');
					
					if (title && title != '') {
						$('<div />', {
							'class' : base.options.titleClass,
							html  : title
						}).appendTo(li);
					}
				}
				
				// Create filmstrip items
				var img = $('<img />', {
					src : li.data('thumbnail'),
					alt : ''
				});
				
				var listItem = $('<li />', {
					'class' : base.options.filmstripItemClass
				}).appendTo(base.filmstrip)
				  .append(img);
				
				img.click(function() {
					var index = $(this).parent().index();
					if (base.currentSlide.index() === index)
						return;
					base.autoplay = false;
					clearTimeout(base.timeout);
					base.showSlide(index);
				});
			});
			
			base.filmstripItems = base.filmstrip.children();
			
			var filmstripWrap = $('<div />', {
				'class' : base.options.filmstripWrapClass
			});
			base.filmstrip.wrap(filmstripWrap);
			base.filmstripWrap = base.filmstrip.parent();
			
			var filmstripContainer = $('<div />', {
				'class' : base.options.filmstripContainerClass
			});
			base.filmstripWrap.wrap(filmstripContainer);
			base.filmstripContainer = base.filmstripWrap.parent();
			
			// Create filmstrip next and previous links
			base.filmstripPrev = $('<div />', {
				'class' : base.options.filmstripPrevClass,
				click   : function() {
					var index = base.currentSlide.index() - 1;
					if (index < 0)
						index = base.slides.length - 1;
					if (base.slides.length === 1)
						return;
					base.autoplay = false;
					clearTimeout(base.timeout);
					base.showSlide(index);
				}
			}).appendTo(base.filmstripContainer);
			
			base.filmstripNext = $('<div />', {
				'class' : base.options.filmstripNextClass,
				click   : function() {
					var index = base.currentSlide.index() + 1;
					if (index > base.slides.length - 1)
						index = 0;
					if (base.slides.length === 1)
						return;
					base.autoplay = false;
					clearTimeout(base.timeout);
					base.showSlide(index);
				}
			}).appendTo(base.filmstripContainer);
			
			base.container.trigger('emrlgallery.built', [base]);
		};
		
		/**
		 * Get the width and height of each slide
		 */
		base.slideDimensions = function() {
			base.slides.each(function(i, li) {
				li = $(li);
				width  = parseInt(li.outerWidth());
				height = parseInt(li.outerHeight());
				li.data('emrlgallery-slide-width', width);
				li.data('emrlgallery-slide-height', height);
			});
		};
		
		/**
		 * Get the width of each filmstrip item, then set the width of the container
		 * to the total width to keep all the items in one line
		 */
		base.filmstripDimensions = function() {
			var totalWidth = 0;
			base.filmstripItems.each(function(i, li) {
				li = $(li);
				width = parseInt(li.outerWidth());
				li.data('emrlgallery-filmstrip-item-width', width);
				totalWidth += width;
			});
			
			base.filmstripIncrement = width;
			
			base.filmstrip.width(totalWidth);
			
			var wrapWidth = base.filmstripWrap.width();
			var numVisible = Math.floor(wrapWidth / base.filmstripIncrement);
			base.filmstripWrap.width(numVisible * base.filmstripIncrement);
			base.slides.find('.' + base.options.titleClass).width((numVisible * base.filmstripIncrement) - 10);
			base.filmstripsVisible = [0, numVisible - 1];
			base.numFilmstripsVisible = numVisible;
		};
		
		/**
		 * Show a slide
		 */
		base.showSlide = function(index) {
			// If we're transitioning between slides, just return, don't transition again.
			if (base.transitioning)
				return;
				
			base.transitioning = true;
			
			// The slide that is being replaced
			var replace = base.currentSlide;
			
			// The slide that we're moving to
			var currentSlide = base.currentSlide = base.slides.eq(index);
			
			// Don't do anything if we're trying to change to the same slide
			if (replaceIndex === index) {
				base.transitioning = false;
				return;
			}
			
			// If we need to replace anything (will happen everytime except for startup)
			if (replace) {
				var replaceIndex = replace.index();
				
				// Fade the old slide, and remove the current class from it and its resective filmstrip item
				replace.fadeOut(base.options.speed).removeClass('current');
				base.filmstripItems.eq(replaceIndex).removeClass('current');
				
				// Get the start and end filmstrip items that are currently visible
				var visStart = base.filmstripsVisible[0];
				var visEnd   = base.filmstripsVisible[1];
				
				// What direction to move the filmstrip
				var modifier;
				
				// How many items to move
				var distance = 0;
				
				// If we're moving forward
				if ((index == visEnd) && (replaceIndex < index)) {
					modifier = '-=';
					// Start out with trying to move a whole "page"
					distance = base.numFilmstripsVisible - 1;
					
					var newEnd = index + distance;
					
					// If the new last item doesn't exist, move back to the last existing one
					if (newEnd > (base.slides.length - 1)) {
						var diff = newEnd - (base.slides.length - 1);
						distance -= diff;
					}
					
					// Set the items that are now visible in the filmstrip
					base.filmstripsVisible = [visStart + distance, visEnd + distance];
				}
				
				// If we're moving backward
				if ((index == visStart) && (index < replaceIndex)) {
					modifier = '+=';
					distance = base.numFilmstripsVisible - 1;
					var newStart = index - distance;
					if (newStart < 0) {
						var diff = Math.abs(newStart);
						distance -= diff;
					}
					base.filmstripsVisible = [visStart - distance, visEnd - distance];
				}
				
				// Restarting (last to first)
				if ((index == 0) && (replaceIndex == (base.slides.length - 1))) {
					modifier = '+=';
					distance = base.slides.length - base.numFilmstripsVisible;
					base.filmstripsVisible = [0, base.numFilmstripsVisible - 1];
				}
				
				// Going from first to last
				if ((index == base.slides.length - 1) && (replaceIndex == 0)) {
					modifier = '-=';
					distance = base.slides.length - base.numFilmstripsVisible;
					base.filmstripsVisible = [base.slides.length - base.numFilmstripsVisible, base.slides.length - 1];
				}
				
				// If we have somewhere to move
				if (distance > 0) {
					// Move in the correct direction, by the number of items multiplied by the width of the items
					base.filmstrip.animate({
						marginLeft: modifier + (distance * base.filmstripIncrement)
					}, base.options.speed);
				}
			}
			
			// Reposition the container to the center
			base.wrap.animate({
				//marginTop  : -(Math.round(parseInt(currentSlide.data('emrlgallery-slide-height')) / 2)),
				//marginLeft : -(Math.round(parseInt(currentSlide.data('emrlgallery-slide-width')) / 2))
				width : currentSlide.data('emrlgallery-slide-width')
			}, base.options.speed);
			
			// Resize the slide's container
			base.slidesWrap.animate({
				width  : currentSlide.data('emrlgallery-slide-width'),
				height : (currentSlide.data('emrlgallery-slide-height')) + 5
			}, base.options.speed);
			
			// Fade the current slide in finally
			currentSlide.fadeIn(base.options.speed, function() {
				base.currentSlide.addClass('current');
				base.filmstripItems.eq(index).addClass('current');
			});
			
			// Set a timeout once all the animations have finished to tell us that we can transition again
			setTimeout(function() {base.transitioning = false;}, base.options.speed);
			
			if (base.autoplay) {
				clearTimeout(base.timeout);
				base.timeout = setTimeout(function() {base.play();}, base.options.interval);
			}
		};
		
		base.play = function() {
			var newIndex = base.currentSlide.index();
			newIndex++;
			console.log(newIndex, base.slides.length - 1);
			if (newIndex > (base.slides.length - 1))
				newIndex = 0;
			base.showSlide(newIndex);
		};
		
		// Run initializer
		base.init();
	};
	
	/**
	 * Defaults
	 */
	$.emrlGallery.defaultOptions = {
		interval            : 0,
		speed               : 500,
		startSlide          : 0,
		containerClass      : 'emrlgallery',
		wrapClass           : 'emrlgallery-wrap',
		slidesWrapClass     : 'emrlgallery-slides-wrap',
		titleClass          : 'emrlgallery-title',
		filmstripContainerClass : 'emrlgallery-filmstrip-container',
		filmstripWrapClass  : 'emrlgallery-filmstrip-wrap',
		filmstripClass      : 'emrlgallery-filmstrip',
		filmstripItemClass  : 'emrlgallery-filmstrip-item',
		filmstripPrevClass  : 'emrlgallery-filmstrip-prev',
		filmstripNextClass  : 'emrlgallery-filmstrip-next',
		currentClass        : 'emrlgallery-current'
	};
	
	/**
	 * Connect to jQuery
	 */
	$.fn.emrlGallery = function(options){
		return this.each(function(){
			(new $.emrlGallery(this, options));
		});
	};
	
})(jQuery);
