(function($) {
	$.jkdGallery = function(el, options) {
		var base = this;
		
		base.el          = el;
		base.$el         = $(el).addClass('jkdGallery-items').wrap('<div class="jkdGallery" />');
		base.$wrapper    = base.$el.closest('.jkdGallery');
		base.initialized = false;
		base.interval    = null;
		
		// specify the first and last item
		base.$el.children(':first').addClass('jkdGallery-first');
		base.$el.children(':last').addClass('jkdGallery-last');
		
		base.$el.data('jkdGallery', base);
		
		// function to initialize the plugin
		base.init = function() {
			base.options = $.extend(true, $.jkdGallery.defaultOptions, options);
			
			if ($.isFunction(base.options.beforeLoad)) {
				base.options.beforeLoad.call(base, base.$wrapper[0]);
			}
			
			if (base.options.container === 'window') {
				base.options.container = $(window);
				if (base.options.overflow.x) {
					$('body').css('overflow-x', 'hidden');
				}
				if (base.options.overflow.y) {
					$('body').css('overflow-y', 'hidden');
				}
			} else {
				if (base.options.overflow.x) {
					base.options.container.css('overflow-x', 'hidden');
				}
				if (base.options.overflow.y) {
					base.options.container.css('overflow-y', 'hidden');
				}
			}
			
			var width  = base.options.container.width()  + base.options.adjust.width;
			var height = base.options.container.height() + base.options.adjust.height;
			
			base.$el
			.width(width)
			.height(height)
			.css('position', 'relative')
			.css('overflow', 'hidden');
			
			base.$wrapper
			.width(width)
			.height(height)
			.css('position', 'relative')
			.css('overflow', 'hidden');
			
			// create prev link
			if (base.options.showPrev) {
				$('<a href="javascript:void(0);"><span>' + base.options.prevText + '</span></a>')
				.bind('click', function() {
					base.prev();
					return false;
				})
				.appendTo(base.$wrapper)
				.wrap('<div class="jkdGallery-prev" />');
			}
			
			// create next link
			if (base.options.showNext) {
				$('<a href="javascript:void(0);"><span>' + base.options.nextText + '</span></a>')
				.bind('click', function() {
					base.next();
					return false;
				})
				.appendTo(base.$wrapper)
				.wrap('<div class="jkdGallery-next" />');
			}
			
			if (base.options.showNav) {
				base.$nav = $('<ul />');
			}
			
			base.$el.children().each(function(index, item) {
				$(item)
				.css('position', 'absolute')
				.css('display', 'none')
				.attr('rel', 'jkdGallery-item-' + (index + 1) )
				.bind('load', function() {
					var $this = $(this);
					
					// position the item centered while keeping the aspect ratio
					if ( (width / ( $this.width() / $this.height() ) ) > height ) {
						$this
						.width(width)
						.css('height', 'auto')
						.css('top', ( ( height - $this.height() ) / 2) + 'px')
						.css('left', 0);
					} else {
						$this
						.height(height)
						.css('width', 'auto')
						.css('left', ( ( width - $this.width() ) / 2) + 'px')
						.css('top', 0);
					}
				});
				
				// add item to navigation
				if (base.options.showNav) {
					$('<a href="javascript:void(0);">' + (index + 1) + '</a>')
					.bind('click', function() {
						base.goto(index + 1);
						return false;
					})
					.appendTo(base.$nav)
					.wrap('<li class="jkdGallery-item-' + (index + 1) + '" />');
				}
			});
			
			// add the navigation
			if (base.options.showNav) {
				$('<div class="jkdGallery-nav" />')
				.append('<div class="jkdGallery-beforeNav" />')
				.append(base.$nav)
				.append('<div class="jkdGallery-afterNav" />')
				.appendTo(base.$wrapper);
				
				// center the navigation
				base.$wrapper
				.find('.jkdGallery-nav')
				.css('left', ((width - base.$wrapper.find('.jkdGallery-nav').width()) / 2) + 'px');
			}
			
			// check if we need to pause on hover
			if (base.options.pauseOnHover) {
				base.$wrapper.bind({
					mouseenter: function() {
						base.stop();
					},
					mouseleave: function() {
						base.start();
					}
				});
			}
			
			// check if we need to add a resize handler to the container
			if (base.options.resize) {
				$(window).bind('resize', function() {
					base.resize(base.options.container.width() + base.options.adjust.width, base.options.container.height() + base.options.adjust.height);
				});
			}
			
			// show the startAt item
			base.goto( base.options.startAt );
			
			// check if we need to loop over the items
			if (base.options.autoLoop) {
				base.start();
			}
			
			if ($.isFunction(base.options.afterLoad)) {
				base.options.afterLoad.call(base, base.$wrapper[0]);
			}
			
			// center the images after they have loaded
			var loadedImgs = 0,
				images = $('img', base.el);
			images.each(function() {
				$(this).bind('load', function() {
					loadedImgs++;
					if (loadedImgs == (images.length - 1)) {
						base.resize(base.options.container.width() + base.options.adjust.width, base.options.container.height() + base.options.adjust.height);
					}
				});
			});
			
			base.initialized = true;
		};
		
		// function to start the loop
		base.start = function() {
			if (base.interval === null) {
				base.interval = window.setInterval(function() {
					base.next();
				}, base.options.interval);
			}
		};
		
		// function to stop the loop
		base.stop = function() {
			if (base.interval !== null) {
				window.clearInterval(base.interval);
				base.interval = null;
			}
		};
		
		// function called onresize
		base.resize = function(width, height) {
			
			if ($.isFunction(base.options.beforeResize)) {
				base.options.beforeResize.call(base, base.$wrapper[0]);
			}
			
			// update the gallery size
			base.$el.width(width);
			base.$el.height(height);
			
			// update the gallery wrapper size
			base.$wrapper.width(width);
			base.$wrapper.height(height);
			
			// re-position the item
			base.$el.children().each(function(index, item) {
				var $this = $(item);
				
				if ( (width / ( $this.width() / $this.height() ) ) > height) {
					$this
					.width(width)
					.css('height', 'auto')
					.css('top', ( ( height - $this.height() ) / 2) + 'px')
					.css('left', 0);
				} else {
					$this
					.height(height)
					.css('width', 'auto')
					.css('left', ( ( width - $this.width() ) / 2) + 'px')
					.css('top', 0);
				}
			});
			
			// re-center the navigation
			if (base.options.showNav) {
				base.$nav
				.closest('.jkdGallery-nav')
				.css('left', ((width - base.$nav.closest('.jkdGallery-nav').width()) / 2) + 'px');
			}
			
			if ($.isFunction(base.options.afterResize)) {
				base.options.afterResize.call(base, base.$wrapper[0]);
			}
		};
		
		// function to show item
		base.show = function($item) {
			if ($.isFunction(base.options.beforeShow)) {
				base.options.beforeShow.call(base, base.$wrapper[0], $item[0]);
			}
			
			// hide current item
			base.$el.children(':visible').css('z-index', 0).fadeOut(base.options.transitionTime * (base.options.transition == 'slide' ? 2 : 1));
			
			var afterShow = function() {
				if ($.isFunction(base.options.afterShow)) {
					base.options.afterShow.call(base, base.$wrapper[0], $item[0]);
				}
			}
			
			// show new item
			if (base.options.transition === 'slide' && base.initialized) {
				$item.css('z-index', 1).slideDown(base.options.transitionTime, afterShow);
			} else {
				$item.css('z-index', 1).fadeIn(base.initialized ? base.options.transitionTime : 500, afterShow);
			}
			
			// update the navigation
			if (base.options.showNav) {
				base.$nav.children().removeClass('jkdGallery-visible');
				base.$nav.children('.' + $item.attr('rel')).addClass('jkdGallery-visible');
			}
		};
		
		// function to show item at index
		base.goto = function(index) {
			base.show( base.$el.children(':nth-child(' + index + ')') );
		};
		
		// function to show next item
		base.next = function() {
			var $current = base.$el.children(':visible');
			base.show( $current.hasClass('jkdGallery-last') ? $current.parent().children(':first-child') : $current.next() );
		};
		
		// function to show previous item
		base.prev = function() {
			var $current = base.$el.children(':visible');
			base.show( $current.hasClass('jkdGallery-first') ? $current.parent().children(':last-child') : $current.prev() );
		};
		
		// call initialize
		base.init();
	};
 	
	$.jkdGallery.defaultOptions = {
		/* size options: */
		container:      'window',                     // 'window' or $(element) to resize gallery to
		adjust: {
			width:      0,                            // adjust gallery width in px
			height:     0                             // adjust gallery height in px
		},
		overflow: {
			x:          true,                         // hide the horizontal overflow (scrollbar)
			y:          true                          // hide the vertical overflow (scrollbar)
		},
		resize:         false,                        // resize gallery on container resize
		
		/* loop options: */
		autoLoop:       false,                        // auto start looping the items
		interval:       5000,                         // loop interval (in miliseconds)
		pauseOnHover:   false,                        // pause loop on hover
		transition:     'fade',                       // 'fade' or 'slide' transition (note: by default the 1st item will fade in, in 500ms)
		transitionTime: 1000,                         // item transition duration (in miliseconds)
		
		/* navigation options: */
		showPrev:       true,                         // show the previous link
		showNext:       true,                         // show the previous link
		showNav:        true,                         // show the navigation
		prevText:       'prev',                       // text for previous link
		nextText:       'next',                       // text for next link
		startAt:        1,                            // gallery item to start at
		
		/*
			callbacks:
			this:    gallery (Object)
			gallery: gallery (HTML Element)
			item:    item    (HTML Element)
		*/
		beforeLoad:     function(gallery) {},         // function called before gallery is loaded
		afterLoad:      function(gallery) {},         // function called after gallery is loaded
		beforeShow:     function(gallery, item) {},   // function called before an item is shown
		afterShow:      function(gallery, item) {},   // function called after an item is shown
		beforeResize:   function(gallery) {},         // function called before gallery is resized
		afterResize:    function(gallery) {}          // function called after gallery is resized
	};
	
	$.fn.jkdGallery = function(options) {
		return this.each(function() {
			(new $.jkdGallery(this, options));
		});
	};
	
})(jQuery);
