var ImageGallery = Class.create({
	initialize: function(imageClass) {
		
		// Get options
		this.options = Object.extend({
			imageDir:		null,
			popupColor:		'#111111',
			popupOpacity:	0.97,
			idleOpacity:	0.85,
			hoverOpacity:	1,
			useCaption:		true
		}, arguments[1] || {} );
		
		// Set member variables
		this.visible = false;
		
		// Make event listeners
		this.bshowImage = this.showImage.bindAsEventListener(this);
		this.bhideImage = this.hideImage.bindAsEventListener(this);
		this.bsetIdle   = this.setIdle.bindAsEventListener(this);
		this.bsetHover  = this.setHover.bindAsEventListener(this);
		
		// Make a cover for the gallery
		this.cover = new PopupCover({color: this.options.popupColor, opacity: this.options.popupOpacity, closeCallback: this.bhideImage});
		
		// Make the necessary nodes
		this.next = null;
		this.prev = null;
		this.close = null;
		this.imageDiv = null;
		this.loading = new Element('div', { id: 'imageGallery_loading', style: 'display:none' });
		this.content = $(Builder.node('div', { id: 'imageGallery_content', style: 'display:none' }, [
			this.imageDiv = new Element('div', { className: 'imageGalery_image' }),
			this.caption  = new Element('div', { className: 'imageGalery_caption' }),
			Builder.node('div', { id: 'imageGallery_bg' }, [
				new Element('div', { className: 'imageGallery_bg imageGallery_bg_n'  }),
				new Element('div', { className: 'imageGallery_bg imageGallery_bg_ne' }),
				new Element('div', { className: 'imageGallery_bg imageGallery_bg_e'  }),
				new Element('div', { className: 'imageGallery_bg imageGallery_bg_se' }),
				new Element('div', { className: 'imageGallery_bg imageGallery_bg_s'  }),
				new Element('div', { className: 'imageGallery_bg imageGallery_bg_sw' }),
				new Element('div', { className: 'imageGallery_bg imageGallery_bg_w'  }),
				new Element('div', { className: 'imageGallery_bg imageGallery_bg_nw' })
			]),
			Builder.node('div', { id: 'imageGallery_controls' }, [
				this.close = new Element('a', { className: 'imageGallery_close', href: 'javascript:;' }),
				this.prev  = new Element('a', { className: 'imageGallery_left',  href: 'javascript:;' }),
				this.next  = new Element('a', { className: 'imageGallery_right', href: 'javascript:;' })
			])
		]));
		
		this.next .observe('click', this.showImage.bind(this,  1));
		this.prev .observe('click', this.showImage.bind(this, -1));
		this.close.observe('click', this.bhideImage);
		$(document.body).insert(this.content);
		$(document.body).insert(this.loading);
		
		// Toggle caption
		if(this.options.useCaption === false) {
			this.caption.hide(); }
		
		// Attach events and make gallery images
		var image_i = 0;
		this.current_i = 0;
		this.imageGallery = new Hash();
		this.imageList = $$('img.' + imageClass);
		this.numImages = this.imageList.length;
		this.imageList.each(function(image) {
			
			// Bind a mouse click event
			image.observe('click', this.bshowImage);
			
			// Only bind the thumb hover functions if the options are set
			if(this.options.idleOpacity != this.options.hoverOpacity)
			{
				image.observe('mouseover', this.bsetHover);
				image.observe('mouseout',  this.bsetIdle);
				image.setOpacity(this.options.idleOpacity);
			}
			
			// Get the file name of the image
			var imageName = this.stripImageName(image.readAttribute('src'));
			var imageObj = new GalleryImage(imageName, {imageDir: this.options.imageDir, loadingDiv: this.loading});
			
			// Store the index of the object in the array
			imageObj.image_i = image_i++;
			
			// Store the image gallery object in the hash
			this.imageGallery.set(imageName, imageObj);
		}, this);
		
		// Attach css dynamically
		if($('_ImageGallery-CSS') === null)
		{
			var rand = Math.round(10000 * Math.random());
			$$('head')[0].insert(new Element('link', { id: "_ImageGallery-CSS", href: "scripts/imageGallery/imageGallery.css?"+rand, rel:"stylesheet", type: "text/css", media: "screen" }));
		}
	},
	
	stripImageName: function(src) {
		var dirs = src.split('/');
		return dirs[dirs.length-1];
	},
	
	setIdle: function(event) {
		var elem = Event.element(event);
		elem.setOpacity(this.options.idleOpacity);
	},
	
	setHover: function(event) {
		var elem = Event.element(event);
		elem.setOpacity(this.options.hoverOpacity);
	},
	
	showImage: function(event) {
		
		// Show the cover
		this.cover.show();
		this.close.show();
		
		// Open a url that was passed to the function
		if(Object.isString(event))
		{
			this.next.hide();
			this.prev.hide();
			this.thumb = null;
			this.imageName = this.stripImageName(event);
			this.imageObj = new GalleryImage(this.imageName, {imageDir: this.options.imageDir, loadingDiv: this.loading});
		}
		
		// Get the selected image gallery object
		else if(this.visible === false)
		{
			this.next.show();
			this.prev.show();
			this.thumb = Event.element(event);
			this.imageName = this.stripImageName(this.thumb.readAttribute('src'));
			this.imageObj = this.imageGallery.get(this.imageName);
			this.current_i = this.imageObj.image_i;
		}
		
		// Get the next image in the list
		else
		{
			this.next.show();
			this.prev.show();
			this.current_i = (this.current_i + event) % this.numImages;
			if(this.current_i < 0) {
				this.current_i = this.numImages - 1; }
			this.thumb = this.imageList[this.current_i];
			this.imageName = this.stripImageName(this.thumb.readAttribute('src'));
			this.imageObj = this.imageGallery.get(this.imageName);
		}
		
		// Get the window position
		this.windowDim = document.viewport.getDimensions();
		this.windowPos = document.viewport.getScrollOffsets();
		
		// Center the loading div
		var loadingDim = this.loading.getDimensions();
		this.loading.setStyle({
			left: (this.windowPos.left+(this.windowDim.width -loadingDim.width) /2).round()+'px',
			top:  (this.windowPos.top +(this.windowDim.height-loadingDim.height)/2).round()+'px'}
		);
		
		// Check to see if the image is already loaded
		if(this.imageObj.loaded == false) {
			this.imageObj.load(this.showImage_2.bind(this)); }
		else {
			this.showImage_2(); }
	},
	
	showImage_2: function() {
		// Hide the image div and put the new image in
		new Effect.Morph(this.imageDiv, {style: { opacity: '0' }, duration: 0.4, queue: { position: 'end', scope: 'imageGalery' },
			afterFinish: this.showImage_3.bind(this)
		});
	},
	
	showImage_3: function() {
		
		// Update image
		this.imageDiv.update(this.imageObj.imageNode('imageGalery_image'));
		var image = $('imageGalery_image');
		
		// Change the caption
		if(this.options.useCaption) {
			this.caption.update(this.imageObj.title()); }
		
		// Store the old dimensions
		var widthOld  = this.width;
		var heightOld = this.height;
		
		// Get the image dimensions
		this.width  = this.imageObj.imageObj.width;
		this.height = this.imageObj.imageObj.height;
		
		// Compare againest the viewport dimensions
		var setWidth  = (this.windowDim.width  - 30) < this.width;
		var setHeight = (this.windowDim.height - 30) < this.height;
		if(setWidth && setHeight)
		{
			var newWidth  = (this.windowDim.height - 30) * (this.width/this.height);
			if(newWidth < this.windowDim.width) {
				setWidth = false; }
			else {
				setHeight = false; }
		}
		
		if(setWidth)
		{
			// Resize the image and get the new height from browser (browsers will retain proportions)
			this.width  = this.windowDim.width - 30;
			image.setStyle({width: this.width+'px'});
			this.height = image.getHeight();
		}
		else if(setHeight)
		{
			// Resize the image and get the new width from browser (browsers will retain proportions)
			this.height  = this.windowDim.height - 30;
			image.setStyle({height: this.height+'px'});
			this.width  = image.getWidth();
		}
		
		// Center the image
		this.left = (this.windowPos.left + ((this.windowDim.width -this.width) /2)).round();
		this.top  = (this.windowPos.top  + ((this.windowDim.height-this.height)/2)).round();
		
		// Set properties
		if(this.visible === false)
		{
			this.content.setStyle({ opacity: '0' });
			if(this.thumb) {
				this.content.clonePosition(this.thumb); }
			else {
				this.content.setStyle({width: '20px', height: '20px', left: this.left+'px', top: this.top+'px'}); }
		}
		
		// Show the image
		this.content.show();
		
		if(this.visible === false || this.width != widthOld || this.height != heightOld)
		{
			new Effect.Morph(this.content, {style: { width: this.width+'px', height: this.height+'px',
				left: this.left+'px', top: this.top+'px', opacity: '1' }, duration: 0.4, queue: { position: 'end', scope: 'imageGalery' }});
		}
		
		new Effect.Morph(this.imageDiv, {style: { opacity: '1' }, duration: 0.4, queue: { position: 'end', scope: 'imageGalery' }});
		
		// Flag as visible
		this.visible = true;
	},
	
	hideImage: function(event) {
		
		// Get the image position
		var imagePos = document.viewport.getDimensions();
		
		// Adjust the position
		imagePos.top = Math.random() * (imagePos.height - this.height*1.3);
		imagePos.left = imagePos.width - this.width*1.3;
		
		// Hide the cover if the close button was pressed
		if(event) {
			this.cover.hide(); }
		
		// The close button looks funny during a close
		this.close.hide();
		
		// Morph back to the image
		var thisObj = this;
		new Effect.Morph(this.content, {style: { width: 20+'px', height: 20+'px',
			left: imagePos.left+'px', top: imagePos.top+'px', opacity: '0' }, duration: 0.4,
			afterFinish: function() {
				thisObj.content.hide();
				thisObj.visible = false;
			}
		});
	}
});

var GalleryImage = Class.create({
	initialize: function(image) {
		
		// Get options
		this.options = Object.extend({
			imageDir:		null,
			loadingDiv:		null,
			loadDelay:		100,
			autoLoad:		false,
			loadCallback:	null
		}, arguments[1] || {} );
		
		// Set member variables
		this.image = image;
		this.loaded = false;
		this.loading = false;
		this.bonLoad = this.onLoad.bindAsEventListener(this);
		
		if(this.options.autoLoad == true) {
			this.load(); }
	},

	onLoad: function() {
		this.loaded = true;
		this.loading = false;
		
		if(this.options.loadingDiv != null && this.loadTimer !== null)
		{
			clearTimeout(this.loadTimer);
			this.loadTimer = null;
			$(this.options.loadingDiv).hide();
		}
		
		if(this.options.loadCallback != null) {
			this.options.loadCallback(); }
	},

	load: function(callback) {
		if(this.loading == true || this.loaded == true) { return; }
		this.loading = true;
		
		if(callback != null) {
			this.options.loadCallback = callback; }
		
		if(this.options.loadingDiv != null)
		{
			var div = $(this.options.loadingDiv);
			this.loadTimer = setTimeout(function(){div.show();}, this.options.loadDelay);
		}
		
		this.imageObj = $(new Image());
		this.imageObj.observe('load', this.bonLoad);
		this.imageObj.writeAttribute({src: this.options.imageDir + this.image});
	},
	
	title: function() {
		var match = /(.+)\.jpg$/.exec(this.image);
		return GalleryImage.strtotitle(match[1]);
	},

	imageNode: function(id) {
		return '<img src="' + this.options.imageDir + this.image + '" ' + (id?'id="'+id+'"':'') + ' />';
	}
});

// Converts $title to Title Case, and returns the result.
GalleryImage.strtotitle = function(title)
{
	var smallwords = new Array( 'of','a','the','and','an','or','nor','but','is','if','then','else','when',
		'at','from','by','on','off','for','in','out','over','to','into','with' );
	var words = title.split(' ');
	
	var length = words.length;
	for(var word_i = 0; word_i < length; ++word_i)
	{
		var word = words[word_i];
		if(word_i == 0 || smallwords.indexOf(word) == -1) {
			words[word_i] = word.capitalize(); }
	}
	
	return words.join(' ');
}
