var openPopupWindows = new Array();

/**
 * Type constants for popup buttons
 * Use them for option _type_ on creating a Button instance
 */
var ButtonType = {
	SUBMIT: 'submit',
	CANCEL: 'cancel',
	RESET: 'reset'
};

/**
 * Popup button to submit or reset forms and to close popup window
 * To add different click behavior extend this class and overwrite onClick method
 */
Button = Class.create(Object.prototype, {
	initialize: function(value) {
		this.options = Object.extend({
			value: value,
			type: ButtonType.SUBMIT,
			onClick: function() {}
		}, arguments[1] || {});

		this.node = Builder.node('input', {'class': 'button ' + this.options.type, type: 'button', value: this.options.value});
		Event.observe(this.node, 'click', this.click.bindAsEventListener(this));	
	},

	click: function(event) {
		var openPopupWindow = openPopupWindows.pop();
		openPopupWindows.push(openPopupWindow);

		var form = $(openPopupWindow.window).down('FORM');
		this.options.onClick(form);
		if(this.options.type == ButtonType.SUBMIT) {			
			if(form) {
				if(openPopupWindow.options.update) {
			     		new Ajax.Updater(openPopupWindow.options.update, form.action, {
							evalScripts: true,
							parameters: Form.serialize(form),
							onComplete: function(transport) {
								Popup.close();
							}
						}
					);
				}
				else {
					new Ajax.Updater(openPopupWindow.body, form.action, {
							evalScripts: true,
							parameters: Form.serialize(form),
							onComplete: function(transport) {
								Popup.refresh();
							}
						}
					);
				}
			}
			else {
				alert('There is no form to submit!');
			}
		}
		else if(this.options.type == ButtonType.CANCEL) {
			openPopupWindow.close();
		}
		else if(this.options.type == ButtonType.RESET) {
			if(form) {
				form.reset();
			}
			else {
				alert('There is no form to reset');
			}
		}
		else {
			this.onClick(event, this.options.type);
		}
	},
	
	onClick: function(event, type) {
		alert('Click behavior for button type "'+type+'" is not implemented yet!');
	}	
});

/**
 * Constants for popup position
 * Use them for option _position_ on creating a Popup instance
 */
var PopupPosition = {
		CENTER: 'center',
		MOUSE: 'mouse'
};

/**
 * 
 */
var Popup = Class.create(Object.prototype, {
	initialize: function(event, content) {
		this.options = Object.extend({
			title: '',
			icon: false,
			content: content,			
			width: '500px',
			position: PopupPosition.MOUSE,
			modal: true,
			sameWindow: false,
			url: false,
			update: false,
			closeOnBackgroundClick: false,
			buttons: [],
			onLoad: function (window) {},
			onClose: function(window) {},
			onRefresh: function(window) {}
		}, arguments[2] || {});
		
		if(this.options.sameWindow) {
			openPopupWindow = openPopupWindows.pop();
			if(openPopupWindow) {
				openPopupWindow.unload();
			}			
		}		

		openPopupWindows.push(this);

		this.button = Builder.node('img', {'class': 'close', src: '/images/icons/cross.png'});
		this.buttons = Builder.node('div', {'class': 'buttons'});
		this.body = Builder.node('div', {id: 'popup_body', 'class': 'body'});
		this.body.innerHTML = this.options.content;

		// Add registered button objects to popup window
		for(var i = 0; i < this.options.buttons.length; i++)
		{
			this.buttons.appendChild(this.options.buttons[i].node);
		}
		
		// Add window icon to title bar
		var title = new Array();		
	    if(this.options.icon){
	    	title.push(Builder.node('img', {'class': 'icon', src: this.options.icon}));
	    }	    
	    title.push(Builder.node('span', {'class': 'name'}, this.options.title));	    
	    
	    // Create html construct for popup window
		this.window = Builder.node('div',{id: 'popup', style: 'visibility: hidden; width: ' + this.options.width},[
			Builder.node('div', {'class': 'tl'}),
			Builder.node('div', {'class': 't'}),
			Builder.node('div', {'class': 'tr'}),			
			Builder.node('div', {'class': 'l'}),
			Builder.node('div', {'class': 'r'}),
			Builder.node('div', {'class': 'bl'}),
			Builder.node('div', {'class': 'b'}),
			Builder.node('div', {'class': 'br'}),			
			Builder.node('div', {'class': 'title'}, title),
			this.button,
			this.body,
			this.buttons
		]);

		// Create modal background layer
		this.background = Builder.node('div', {id: 'popup_modal_layer', style: 'display: none'});
		if (this.options.closeOnBackgroundClick) {
			this.background.setAttribute('class','closeable');
		}

		// Register events
		Event.observe(window, 'resize', this.refresh.bindAsEventListener(this));
		Event.observe(window, 'scroll', this.scroll.bindAsEventListener(this));
		Event.observe(document, 'keyup', this.keyup.bindAsEventListener(this));
		if (this.options.closeOnBackgroundClick) {
			Event.observe(this.background, 'click', this.close.bindAsEventListener(this));
		}
		Event.observe(this.button, 'click', this.close.bindAsEventListener(this));
		
		// Append popup window and modal background to body
		document.getElementsByTagName('body')[0].appendChild(this.background);
		document.getElementsByTagName('body')[0].appendChild(this.window);

		this.options.onLoad(this);

		// Show modal background if needed
		if(this.options.modal) {
			this.background.show();
		}
		
		// Set opening position according to mouse position
		if(this.options.position == PopupPosition.MOUSE)
		{
			this.moveToMousePosition(event);			
		}
		this.refresh();	
		
		// Load popup content with ajax and reposition popup
		if(this.options.url)
		{
			new Ajax.Updater(this.body, this.options.url, {
				evalScripts: true,
				popup: this,				
				onComplete: function(transport) {
					var popup = transport.request.options.popup;				
					if(popup.options.position == PopupPosition.MOUSE)
					{
						popup.moveToMousePosition(false);			
					}
					popup.refresh();
				}
			});
		}		
	},

	close: function(event) {
		// Hide popup window and modal layer
		this.window.hide();
		this.background.hide();
		
		// Unload current popup window
		this.unload();
	},

	keyup: function(event) {
		if(!event)
			event = window.event;
		
		var code = 0;		
		if (event.which) {
			code = event.which;
		} else if (event.keyCode) {
			code = event.keyCode;
		}
						
		// Close popup window if ESC button was pressed on keyboard
		if(code == 27) {
			this.close();
		}else if(code == 13){			
			if( !event.element() || !(event.element() instanceof HTMLTextAreaElement)){
			   var firstButton = $A(this.buttons.childNodes).first();
			   if(firstButton){
			      firstButton.click();
			   }
			}	
		}
	},
	
	scroll: function(event) {
		if(this.options.modal) {
			
			// Move modal layer to current viewport position
			var viewportDimensions = document.viewport.getDimensions();	
			var scrollOffset = document.viewport.getScrollOffsets();
			
			this.background.style.top = scrollOffset.top + 'px';
			this.background.style.left = scrollOffset.left + 'px';
			this.background.style.height = viewportDimensions.height + 'px'; 
			this.background.style.width = viewportDimensions.width + 'px';
		}
	},

	refresh: function() {
		this.options.onRefresh(this);
		if(this.window) {			
					
			// Calculate popup position in the center of viewport
			if(this.options.position == PopupPosition.CENTER)
			{
				var scrollOffset = document.viewport.getScrollOffsets();
				var borderSize = this.getBorderSize();
				
				var windowPositionTop = scrollOffset.top + ((document.viewport.getHeight()/2) - (this.window.offsetHeight/2));
				var windowPositionLeft = scrollOffset.left + ((document.viewport.getWidth()/2) - (this.window.offsetWidth/2));
				
				// If viewport is smaller than popup window, place popup in topper left corner
				if(windowPositionTop - borderSize.top < scrollOffset.top)
				{
					windowPositionTop = scrollOffset.top + borderSize.top;
				}
				
				if(windowPositionLeft - borderSize.left < scrollOffset.left)
				{
					windowPositionLeft = scrollOffset.left + borderSize.left;
				}
				
				this.window.style.top = windowPositionTop + 'px';
				this.window.style.left = windowPositionLeft + 'px';				
			}				
			this.window.style.visibility = 'visible';
			
			// position modal background if available
			this.scroll();
		}
	},
	
	moveToMousePosition: function(event) {
		if(!event && event != false)
			event = window.event;
		
		if(!event)
		{
			// Set virtual mouse position to current position 
			// Only used for second position adjustments after loading popup content with ajax
			var windowOffset = Element.cumulativeOffset(this.window);
			var mouseOffsetTop = windowOffset.top;
			var mouseOffsetLeft = windowOffset.left;
		}
		else
		{
			// Set position based on mouse position on popup open
			var mouseOffsetTop = Event.pointerY(event);
			var mouseOffsetLeft = Event.pointerX(event);
		}
			
		var windowOffsetLeft = 0;
		var windowOffsetTop = 0;
				
		var scrollOffset = document.viewport.getScrollOffsets();
		var viewportDimensions = document.viewport.getDimensions();		
		var borderSize = this.getBorderSize();		
			
		// Correction of mouse click position if coordinates are too narrow at the browser border
		if(mouseOffsetTop - borderSize.top < scrollOffset.top) {
			mouseOffsetTop = scrollOffset.top + borderSize.top;
		}
		
		if(mouseOffsetLeft - borderSize.left < scrollOffset.left) {
			mouseOffsetLeft = scrollOffset.left + borderSize.left;
		}

		if(this.window.offsetHeight + borderSize.top + borderSize.bottom > viewportDimensions.height)
		{		
			// If popup window is heigher than viewport, set position to viewports top border
			mouseOffsetTop = scrollOffset.top + borderSize.top;
		}		
		else if(mouseOffsetTop + this.window.offsetHeight + borderSize.bottom > scrollOffset.top + viewportDimensions.height)
		{
			// If popup window overlaps viewports bottom border, correct top position to fit
			windowOffsetTop = scrollOffset.top + viewportDimensions.height - mouseOffsetTop - this.window.offsetHeight - borderSize.bottom;
		}
				
		if(this.window.offsetWidth + borderSize.left + borderSize.right > viewportDimensions.width)
		{	
			// If popup window is wider than viewport, set position to viewports left border
			mouseOffsetLeft = scrollOffset.left + borderSize.left;
		}		
		else if(mouseOffsetLeft + this.window.offsetWidth + borderSize.right > scrollOffset.left + viewportDimensions.width)
		{
			// If popup window overlaps viewports right border, correct left position to fit
			windowOffsetLeft = scrollOffset.left + viewportDimensions.width - mouseOffsetLeft - this.window.offsetWidth - borderSize.right;
		}
		
		this.window.style.top = (mouseOffsetTop + windowOffsetTop) + 'px';
		this.window.style.left = (mouseOffsetLeft + windowOffsetLeft) + 'px';
	},

	getBorderSize: function() {
		if(this.window)
		{
			// Calculates border sizes needed for window positioning
			return {
				top: Element.getHeight(this.window.down('.t')),
				right: Element.getWidth(this.window.down('.r')),
				bottom: Element.getHeight(this.window.down('.b')),
				left: Element.getWidth(this.window.down('.l'))			
			};
		}
		// If there's no window there is no border :-)
		return {top: 0, right: 0, bottom: 0, left: 0};
	},
	
	unload: function() {
		this.options.onClose(this);
		
		// Unregister event observation
		Event.stopObserving(window, 'resize', this.refresh.bindAsEventListener(this));
		Event.stopObserving(window, 'scroll', this.scroll.bindAsEventListener(this));
		Event.stopObserving(document, 'keyup', this.keyup.bindAsEventListener(this));

		// Total removal of popup windows html structure 
		this.window.parentNode.removeChild(this.window);
		this.background.parentNode.removeChild(this.background);
		
		for(var i = 0; i < openPopupWindows.length; i++)
		{
			if(openPopupWindows[i] == this)
			{
				openPopupWindows.splice(i, 1);
				break;
			}
		}
	}	
});

/**
 * Static function to close open popup window if one is open
 */
Popup.close = function() {
	var openPopupWindow = openPopupWindows.pop();
	if(openPopupWindow) 
	{
		openPopupWindow.unload();
	}
};

/**
 * Static function to reposition popup window if one is open
 */
Popup.refresh = function() {
	var openPopupWindow = openPopupWindows.pop();
	if(openPopupWindow) 
	{
		openPopupWindows.push(openPopupWindow);
		openPopupWindow.refresh();		
	}
};