var AJZEvents = function() {
	if(!AJZEvents.config.AJAX_URL) {
		throw "AJZEvents not configured!";
	}
	var overlay = createOverlay();
	var visible = false;
	var eventData = {};
	var that = this; // Used to call member functions within helper functions .. ;)
	
	function createOverlay() {
		// Create content element ..
		var content = new Element('div',{'id': 'overlay_content', 'class': 'events'});
		
		var closeLink = new Element('a',{'href': 'JavaScript:;', 'title': 'Close'});
		closeLink.addClassName('close_link');
		closeLink.appendChild(new Element('span').update('X'));
		closeLink.observe('click', function(e) {
			that.setVisible(false);
			Event.stop(e);
		});
		content.appendChild(closeLink);
		
		// Create Images Container ..
		var imageContainer = createImageContainer();
		content.appendChild(imageContainer);
		// Create Info Container ..
		var infoContainer = createInfoContainer();
		content.appendChild(infoContainer);
		// Create Navigation ..
		var navigation = createNavigation();
		content.appendChild(navigation);
		// Create overlay and add content ..
		var overlay = new Element('div',{'id': 'overlay'});
		overlay.updatePosition = function() {
			// Get "Window"-height/width ..
			var vpHeight = overlay.getHeight();
			// Calculate new size for overlay content ..
			var maxHeight = 600;
			var newHeight = vpHeight>maxHeight ? maxHeight : vpHeight;
			content.style.height = newHeight + 'px';
			content.style.marginTop = Math.round((vpHeight - newHeight) / 2) + 'px';
			//alert(newHeight + ", "+content.getHeight());
			// Scale content ..
			this.image_container.updateSize();
		}
		overlay.setData = function(data) {
			window.location.hash = '#'+data['date'];
			this.image_container.setImages(data.images);
			this.info_container.setInfos(data);
			this.navigation.setEvents(data);
		}
		overlay.appendChild(content);
		// Create "shortcuts" ..
		overlay.content_panel = content;
		overlay.image_container = imageContainer;
		overlay.info_container = infoContainer;
		overlay.navigation = navigation;
		return overlay;
	}
	
	function createImageContainer() {
		var imageContainer = new Element('div').addClassName('image_container');
		
		imageContainer.removeAll = function() {
			var next, toRemove = this.firstChild;
			while(toRemove) {
				next = toRemove.nextSibling;
				this.removeChild(toRemove);
				toRemove = next;
			}
			this.mainImg = null;
		}

		imageContainer.updateSize = function() {
			// If there are some images:
			// 1.) Main Image gets 80% of total height
			// 2.) Selection Container Height: 20% of total height
			if(this.mainImg) {
				// Update main image size ..
				var height = this.getHeight() - 20;
				var maxSelectionImgHeight = Math.round(0.2 * height);
				var maxImgHeight = height - maxSelectionImgHeight;
				this.mainImg.resize(this.getWidth()-10, maxImgHeight);
				// Update main image position ..
				this.mainImg.style.top = Math.round((maxImgHeight - this.mainImg.height) / 2)+'px';
				if(this.selection_element.config.loaded==this.selection_element.config.count) {
					// All selection images loaded .. -> update selection images size ...
					var selectionWidth = this.selection_element.getWidth() - 20;
					var maxSelectionImgWidth = Math.floor(selectionWidth / this.selection_element.config.count);
					var selectionImages = this.selection_element.config.images;
					//alert(maxSelectionImgWidth+", "+maxSelectionImgHeight);
					for(var i=0; i<selectionImages.length; ++i) {
						selectionImages[i].resize(maxSelectionImgWidth, maxSelectionImgHeight);
					}
				}
			}
		}
		
		imageContainer.setImages = function(images) {
			this.removeAll();
			// Add Images ..
			this.imageInfo = images;
			if(images && images.length>0) {
				this.mainImg = loadImage(images[0].thumb);
				this.mainImg.style.position = 'relative';
				this.appendChild(this.mainImg);
				var selectionContainer = new Element('div');
				this.selection_element = selectionContainer;
				this.appendChild(selectionContainer);
				selectionContainer.className = 'selection';
				selectionContainer.config = {
					count: images.length,
					loaded: 0,
					images: []
				}
				// Create & Add links & images ..
				for(var i=0; i<images.length; ++i) {
					var imgLink = new Element('a',{'href': 'JavaScript:;'});
					var img = loadImage(images[i].thumb, createImageLoadedAction(imageContainer, imgLink));
					img.toMain = imgToMain;
					img.observe('mouseover', imgMouseOver);
					img.observe('mouseout', imgMouseOut);
					img.observe('click', imgMouseClick);
					if(i==0) {
						img.addClassName('active');
					}
					selectionContainer.appendChild(imgLink);
				}
			} else {
				this.mainImg = null;
				this.selection_element = null;
				// TODO: Hide images container .. ?
			}
		}
				
		return imageContainer;
		
		function loadImage(url, callback) {
			var img = new Element('img');
			img.resize = resizeImageFnc;
			var myCallback = function() {
				this.stopObserving('load', myCallback);
				this.originalSize = {'width': this.width, 'height': this.height};
				if(callback) {
					callback.call(this);
				}
			};
			img.observe('load', myCallback);
			img.src = url;
			return img;
		}

		function resizeImageFnc(maxWidth, maxHeight) {
			if(!this.originalSize) {
				this.originalSize = {'width': this.width, 'height': this.height};
			}
			var width = this.originalSize.width;
			var height = this.originalSize.height;
			var destWidth = maxWidth > width ? width : maxWidth;
			var destHeight = maxHeight > height ? height : maxHeight;
			var factor = Math.min(destHeight / height, destWidth / width);
			this.width = Math.round(width * factor);
			this.height = Math.round(height * factor);
		}
		
		function createImageLoadedAction(container, element) {
			return function() {
				var maxHeight = Math.round((container.getHeight()-20)*0.2);
				var maxWidth = Math.floor((container.selection_element.getWidth()-20) / container.selection_element.config.count);
				this.resize(maxWidth,maxHeight);
				element.appendChild(this);
				container.selection_element.config.images.push(this);
				if(++container.selection_element.config.loaded==container.selection_element.config.count) {
					container.updateSize();
					//alert(this.src+', Width: '+this.width+', Original Width: '+this.originalSize.width);
				}
			}
		}
		
		/* Image-Selection function definitions .. */
		function imgToMain() {
			imageContainer.mainImg.src = this.src;
			imageContainer.mainImg.originalSize = this.originalSize;
			// Calculate correct size & update position ..
			var maxImgHeight = Math.round((imageContainer.getHeight()-20)*0.8);
			imageContainer.mainImg.resize(imageContainer.getWidth()-10, maxImgHeight);
			// Update main image position ..
			imageContainer.mainImg.style.top = Math.round((maxImgHeight - imageContainer.mainImg.height) / 2)+'px';
		}
		
		function imgMouseOver() {
			if(this.src!=imageContainer.mainImg.src) {
				// Save values ...
				this.prev = {
					src: imageContainer.mainImg.src,
					size: [imageContainer.mainImg.width, imageContainer.mainImg.height],
					origSize: imageContainer.mainImg.originalSize,
					top: imageContainer.mainImg.style.top
				};
				this.toMain();
			}
		}
		
		function imgMouseOut() {
			if(this.prev && this.src==imageContainer.mainImg.src) {
				imageContainer.mainImg.src = this.prev.src;
				imageContainer.mainImg.originalSize = this.prev.origSize;
				imageContainer.mainImg.width = this.prev.size[0];
				imageContainer.mainImg.height = this.prev.size[1];
				imageContainer.mainImg.style.top = this.prev.top;
				this.prev = null;
			}
		}
		
		function imgMouseClick() {
			if(this.src==imageContainer.mainImg.src) {
				this.prev = null;
			} else {
				// Set main image to current image ..
				this.toMain();
			}
			var allImages = imageContainer.selection_element.config.images;
			if(!this.hasClassName('active')) {
				for(var i=0; i<allImages.length; ++i) {
					if(allImages[i]!=this && allImages[i].hasClassName('active')) {
						allImages[i].removeClassName('active');
						break;
					}
				}
				this.addClassName('active');
			}
			this.parentNode.blur();
		}
	}
	
	function createInfoContainer() {
		var infoContainer = new Element('div').addClassName('info_container');
		infoContainer.setInfos = function(data) {
			this.displayElements.date.update(data.date_text);
			this.displayElements.title.update(data.title);
			this.displayElements.headline.update(data.headline);
			if(data.organizer) {
				this.displayElements.organizer.update(data.organizer);
				this.displayElements.organizer.show();
			} else {
				this.displayElements.organizer.update('');
				this.displayElements.organizer.hide();
			}
			this.displayElements.text.update(data.text);
			this.displayElements.lineUp.update(data.line_up);
			this.displayElements.details.update(data.details);
		}
		// Create & Add Texts/Info ..
		infoContainer.displayElements = {};
		infoContainer.displayElements.date = new Element('div').addClassName('date');
		infoContainer.appendChild(infoContainer.displayElements.date);
		infoContainer.displayElements.organizer = new Element('div').addClassName('organizer');
		infoContainer.appendChild(infoContainer.displayElements.organizer);
		infoContainer.displayElements.title = new Element('h1');
		infoContainer.appendChild(infoContainer.displayElements.title);
		infoContainer.displayElements.headline = new Element('div').addClassName('headline');
		infoContainer.appendChild(infoContainer.displayElements.headline);
		infoContainer.displayElements.text = new Element('div').addClassName('text');
		infoContainer.appendChild(infoContainer.displayElements.text);
		infoContainer.displayElements.lineUp = new Element('div').addClassName('lineup');
		infoContainer.appendChild(infoContainer.displayElements.lineUp);
		infoContainer.displayElements.details = new Element('div').addClassName('details');
		infoContainer.appendChild(infoContainer.displayElements.details);
		return infoContainer;
	}
	
	function createNavigation() {
		var navigation = new Element('div');
		navigation.className = 'navigation';
		navigation.backLink = new Element('a',{'href': 'JavaScript:;'}).addClassName('prev_link').update('&lt;&lt;');
		navigation.appendChild(navigation.backLink);
		navigation.nextLink = new Element('a',{'href': 'JavaScript:;'}).addClassName('next_link').update('&gt;&gt;');
		navigation.appendChild(navigation.nextLink);
		
		navigation.backLink.observe('click', loadEvent);
		navigation.nextLink.observe('click', loadEvent);
		
		function loadEvent() {
			// 'this' refers to the element, the action was invoked (-> the link) ..
			if(this.current_eventId) {
				that.loadEvent(this.current_eventId);
			}
			this.blur();
		}
		
		navigation.setEvents = function(data) {
			if(data.prev_event) {
				this.setBackLink(data.prev_event.id, data.prev_event.title);
			} else {
				this.setBackLink();
			}
			if(data.next_event) {
				this.setNextLink(data.next_event.id, data.next_event.title);
			} else {
				this.setNextLink();
			}
		}
		navigation.setBackLink = function(eventId, title) {
			updateLink(this.backLink, eventId, title);
		}
		navigation.setNextLink = function(eventId, title) {
			updateLink(this.nextLink, eventId, title);
		}
		
		function updateLink(link, eventId, title) {
			link.current_eventId = eventId;
			if(!eventId) {
				link.title = '';
				link.style.visibility = 'hidden';
			} else {
				link.title = title;
				link.style.visibility = 'visible';
			}
		}
		
		return navigation;
	}
	
	this.loadEventByDate = function(date) {
		// Request Data using AJAX
		requestData('event_date='+date, function(data) {
			if(data) {
				// Cache data ..
				eventData['event_'+data.id] = data;
				// Update Overlay 
				overlay.setData(data);
			} else {
				// TODO: Handle failure ...
			}
		});
	}
	
	this.loadEvent = function(eventId) {
		if(eventId) {
			if(eventData['event_'+eventId]) {
				// Data already requested .. -> Update Overlay 
				overlay.setData(eventData['event_'+eventId]);
			} else {
				// Request Data using AJAX
				requestData('event_id='+eventId, function(data) {
					if(data) {
						// Cache data ..
						eventData['event_'+eventId] = data;
						// Update Overlay 
						overlay.setData(data);
					} else {
						// TODO: Handle failure ...
					}
				});
			}
		}
	}
	
	function requestData(params, callback) {
		// Use a cofiguration in order to determine the AJAX-URL ..
		var requestURL = AJZEvents.config.AJAX_URL;
		new Ajax.Request(requestURL/*'./ajax.php'*/, {
			method: 'get',
			parameters: params,
			requestHeaders: {Accept: 'application/json'},
  			onSuccess: function(response) {
  				callback(response.responseJSON);
			},
			onFailure: function(response) {
				callback(null); // Just pass null ..
			}
		});
	}
	
	this.setVisible = function(state) {
		if(visible!=state) {
			visible = state;
			if(visible) {
				// Add to document body ..
				document.body.appendChild(overlay);
				overlay.updatePosition();
				// Add listener for window resizing ..
				Event.observe(window, 'resize', resizeCallback);
				// Add click-listener observing clicks in background ..
				Event.observe(document, 'mousedown', closeOverlay);
			} else {
				Event.stopObserving(document, 'mousedown', closeOverlay);
				Event.stopObserving(window, 'resize', resizeCallback);
				document.body.removeChild(overlay);
				window.location.hash = '#';
			}
		}
	}
	
	function resizeCallback() {
		if(visible) {
			overlay.updatePosition();
		}
	}
	
	function closeOverlay(e) {
		var element = Event.element(e);
		var current = element;
		if(element && element.tagName == 'HTML') {
			// If user clicks on scrollbars -> don't hide overlay ..
			return;
		}
		while(current) {
			if(current==overlay.content_panel) {
				// Click was on content panel ...
				return;
			}
			current = current.parentNode
		}
		// Consume event / stop further propagation ...
		Event.stop(e);
		that.setVisible(false);
	}
}

AJZEvents.showEvent = function(eventId) {
	if(this.instance) {
		this.instance.setVisible(true);
		this.instance.loadEvent(eventId);
	}
}

AJZEvents.config = {};

// Attach onload handler ...
Event.observe(window, 'load', function() {
	AJZEvents.instance = new AJZEvents();
	var ref = window.location.hash;
	if(ref && ref.match(/^\#\d\d?\.\d\d?\.(\d\d)?\d\d$/)) {
		AJZEvents.instance.setVisible(true);
		AJZEvents.instance.loadEventByDate(ref.substr(1));
	}
});