var Popup = Class.create();

Popup.prototype = {
	objPopup	: Object, 
	objElement	: null,
	showFlag	: false,
	objEvent	: Object,
	overlayObj	: Object,
	manualFlag	: false,
	funcAutoCloseHandler : null,
	afterOpen: Prototype.emptyFunction,
	afterClose: Prototype.emptyFunction,
	initialize: function(options) {
		this.setOptions(options);
		this.objElement = this.getElement();
		this.afterOpen = this.options.afterOpen;
		this.afterClose = this.options.afterClose;
		this.createPopup();
		this.funcAutoCloseHandler = this.hidePopupForClickAndManual.bind(this);
		if (this.options.alwaysCentered == true) {
			Event.observe(window, 'scroll', function() {
				this.updateXY();
			}.bind(this), false);
			Event.observe(window, 'resize', function() {
				this.updateXY();
			}.bind(this), false);
		}
	},
	setOptions: function(options) {
		this.options = {
			targetId			: '',
			contentId			: '',
			closeControlIds		: [],
			opacity				: 0.8,
			triggerEvent		: 'hover',
			autoClose			: true,
			afterOpen			: Prototype.emptyFunction,
			afterClose			: Prototype.emptyFunction,
			alwaysCentered		: false
		};
		Object.extend(this.options, options || {});
	},
	createPopup: function() {
		if (this.objElement != null) {
			if (this.options.triggerEvent == 'hover') {
				this.createPopupForHoverTriggerType();
			} else if (this.options.triggerEvent == 'click') {
				this.createPopupForClickTriggerType();
			} 
		} else {
			this.createPopupForManualTriggerType();
		}

		var len = this.options.closeControlIds.length;
		
		for (i = 0; i < len; i++) {
			var control = document.getElementById(this.options.closeControlIds[i]);
			if (control) {
				Event.observe(control, 'click', this.hidePopupForClickAndManual.bind(this));
			}
		}
	},
	createPopupDiv: function() {
		this.objPopup = document.createElement('div');
		Element.extend(this.objPopup);
		this.objPopup.id = ('tip_' + this.options.targetId);
		this.objPopup.setStyle({
			top: 0, 
			position: 'absolute', 
			'z-index': 1000
		});
		document.getElementsByTagName('body')[0].appendChild(this.objPopup);
		
		this.objPopup.hide();
		var objContent = $(this.options.contentId);
		if (objContent) {
			this.objPopup.appendChild(objContent);
			if (objContent.show) {
				objContent.show();
			}
		}
		
	},
	createPopupForClickTriggerType: function() {
		this.createPopupDiv();
		Event.observe(this.objElement, 'click', this.showPopupForClick.bind(this));
	},
	createPopupForHoverTriggerType: function() {
		this.createPopupDiv();		
		Event.observe(this.objElement, 'mouseover', this.showPopupForHover.bind(this));
		Event.observe(this.objElement, 'mouseout', this.hidePopupForHover.bind(this));
	},
	createPopupForManualTriggerType: function() {
		this.createPopupDiv();
	},
	updateXY: function(e) {
		var cursor;
		if (this.options.alwaysCentered == true || !e) {
			cursor = this.getCenteredPosition();
		} else {
			cursor = this.getCursorPosition(e);
		}
		
		this.objPopup.setStyle({
			left: ((cursor.x + 10) + 'px'),
			top: (cursor.y + 'px')
		});
	},
	showPopupForHover: function(e) {
		if (!this.showFlag) {
			this.updateXY(e);
			this.objPopup.show();
			this.showFlag = true;
			Event.observe(this.objElement, 'mousemove', this.updateXY.bind(this));
		}
	},
	hidePopupForHover: function(e) {
		if (this.showFlag) {
			this.objElement.mousemove = null;
			this.objPopup.hide();
			this.showFlag = false;
			this.options.afterClose(this);
		}
	},
	refresh: function() {
		// this method is useful to workaround IE rendering issue.
		this.objPopup.hide();
		this.updateXY();
		this.objPopup.show();
	},
	showPopup: function(e) {
		if (!this.showFlag) {
			this.manualFlag = true;
			this.updateXY(e);
			this.objPopup.show();
			this.showFlag = true;
			this.objEvent = e;
			
			if (this.options.autoClose) {
				this.objAutoCloseEvent = Event.observe(document, 'click', this.funcAutoCloseHandler);
			}
			this.options.afterOpen(this);
		}
	},
	hidePopup: function() {
		if (this.objElement != null) {
			if (this.options.triggerEvent == 'click') {
				this.hidePopupForClickAndManual();
			} else if (this.options.triggerEvent == 'hover') {
				this.hidePopupForHover();
			}
		} else {
			this.hidePopupForClickAndManual();
		}
	},
	showPopupForClick: function(e) {
		if (!this.showFlag) {
			this.updateXY(e);
			this.objPopup.show();
			this.showFlag = true;
			this.objEvent = e;
			
			if (this.options.autoClose) {
				Event.observe(document, 'click', this.funcAutoCloseHandler);
			}
			this.options.afterOpen(this);
		}
	},
	hidePopupForClickAndManual: function(e) {
		if (this.manualFlag == true) {
			this.manualFlag = false;
			// return;
		}
		var target;
		if (e) {
			if (e.srcElement) {
				target = e.srcElement;
			} else {
				target = e.target;
			}
		}
		
		if (this.isContainedBy(target, this.objElement)) {
			return;
		}
		if (this.showFlag && (!e || this.isCloseControl(target)
			|| !this.isContainedBy(target, this.objPopup))) {
			this.objPopup.hide();
			this.showFlag = false;
			if (this.options.autoClose) {
				// window.onclick = null;
				Event.stopObserving(document, 'click', this.funcAutoCloseHandler);
			}
			this.options.afterClose(this);
		}
	},
	getElement: function() {
		var elm = document.getElementById(this.options.targetId);
		if (elm) {
			Element.extend(elm);
		}
		return elm;
	},
	isContainedBy: function(containee, container) {
		var value = false;
		if (container) {
			while (containee) {
				if (containee.id == container.id) {
					value = true;
					break;
				}
				containee = containee.parentNode;
			}
		}
		return value;
	},
	isCloseControl: function(e) {
		var len = this.options.closeControlIds.length;
		for (i = 0; i < len; i++) {
			if (e && e.id && e.id == this.options.closeControlIds[i]) {
				return true;
			}
		}
		return false;
	},
	isOnTop: function(e) {
		var cursor = this.getCursorPosition(e);
		var dim = this.getDimension(this.objElement);
		if (cursor.x < dim.x || cursor.x > (dim.x + dim.width)
			|| cursor.y < dim.y || cursor.y > (dim.y + dim.height)) {
			return false;	
		}
		return true;
	},
	getCursorPosition: function(e) {
	    e = e || window.event;
	    var cursor = {x:0, y:0};
	    if (e.pageX || e.pageY) {
	        cursor.x = e.pageX;
	        cursor.y = e.pageY;
	    } 
	    else {
	        var de = document.documentElement;
	        var b = document.body;
	        cursor.x = e.clientX + 
	            (de.scrollLeft || b.scrollLeft) - (de.clientLeft || 0);
	        cursor.y = e.clientY + 
	            (de.scrollTop || b.scrollTop) - (de.clientTop || 0);
	    }
	    return cursor;
    },
    getCenteredPosition: function() {
    	var cursor = {x: 0, y: 0};
  		var windowWidth = 0, windowHeight = 0;
  		
		if( typeof( window.innerWidth ) == 'number' ) {
    		//Non-IE
    		windowWidth = window.innerWidth;
    		windowHeight = window.innerHeight;
  		} else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
    		//IE 6+ in 'standards compliant mode'
    		windowWidth = document.documentElement.clientWidth;
    		windowHeight = document.documentElement.clientHeight;
  		} else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
    		//IE 4 compatible
    		windowWidth = document.body.clientWidth;
    		windowHeight = document.body.clientHeight;
  		}
  		
      	var de = document.documentElement;
    	var b = document.body;
    	
    	cursor.x = ((windowWidth - this.objPopup.getWidth())/2) + (de.scrollLeft || b.scrollLeft) - (de.clientLeft || 0);
    	cursor.y = ((windowHeight - this.objPopup.getHeight())/2) + (de.scrollTop || b.scrollTop) - (de.clientTop || 0);
    	
    	return cursor;
    },
    getDimension: function(elm) {
    	var dimension = {x: 0, y: 0, width: 0, height: 0};
    	dimension.x = elm.offsetLeft;
    	dimension.y = elm.offsetTop;
    	dimension.width = elm.offsetWidth;
    	dimension.height = elm.offsetHeight;
    	return dimension;
    },
    isOpened: function() {
    	return this.showFlag;
    }
};

var TreeItem = Class.create();
TreeItem.prototype = {
	id: '',
	attrs: {},
	childItemControls: null,
	elmItem: null,
	elmChildItemsRoot: null,
	getLabelCallback: null,
	getElementIdCallback: null,
	getElementClassNameCallback: null,
	getItemActionsCallback: null,
	onClickCallback: null,
	isExpanded: true,
	isSelected: false,
	treeControl: null,
	elmItemPlaceHolder: null,
	initialize: function(options) {
		this.setOptions(options);
		
		this.id = this.options.data['id'];
		this.label = this.options.data['label'];
		this.attrs = this.options.data['attrs'];
		this.treeControl = this.options.treeControl;
		this.treeControl.itemControlsMap[this.id] = this;
		
		this.getLabelCallback = this.options.getLabelCallback;
		this.getElementIdCallback = this.options.getElementIdCallback;
		this.getElementClassNameCallback = this.options.getElementClassNameCallback;
		this.onClickCallback = this.options.onClickCallback;
		this.getItemActionsCallback = this.options.getItemActionsCallback;
		
		this.initItem();
		this.initChildItems();
	},
	initItem: function() {
		this.elmItem = document.createElement('li');
		Element.extend(this.elmItem);
		this.elmItem.id = this.getElementIdCallback(this.id, this.attrs);
		
		this.elmItemPlaceHolder = document.createElement('div');
		Element.extend(this.elmItemPlaceHolder);
		this.elmItem.appendChild(this.elmItemPlaceHolder);
		
		this.elmItemText = document.createElement('label');
		Element.extend(this.elmItemText);
		this.elmItemPlaceHolder.appendChild(this.elmItemText);
		
		var elmItemActionArray = this.getItemActionsCallback(this.id, this.attrs);
		if (elmItemActionArray && elmItemActionArray.length > 0) {
			var i = 0;
			for (i = 0; i < elmItemActionArray.length; i++) {
				this.elmItemPlaceHolder.appendChild(document.createTextNode(' '));
				this.elmItemPlaceHolder.appendChild(elmItemActionArray[i]);
			}
		}
		
		Event.observe(this.elmItemText, 'click', this.onItemClick.bind(this), false);
	},
	initChildItems: function() {
		var childItems = this.options.data['childItems'];
		if (childItems != undefined && childItems.length > 0) {
			this.childItemControls = new Array();
			
			this.elmChildItemsRoot = document.createElement('ul');
			Element.extend(this.elmChildItemsRoot);
			this.elmItem.appendChild(this.elmChildItemsRoot);
			
			var i = 0;
			var length = childItems.length;
			for (i = 0; i < length; i++) {
				var childItem = childItems[i];
				var childItemControl = new TreeItem({
					data: childItem,
					getLabelCallback: this.getLabelCallback,
					getElementIdCallback: this.getElementIdCallback,
					getElementClassNameCallback: this.getElementClassNameCallback,
					onClickCallback: this.onClickCallback,
					getItemActionsCallback: this.getItemActionsCallback,
					treeControl: this.treeControl
				});
				this.elmChildItemsRoot.appendChild(childItemControl.elmItem);
				this.childItemControls[i] = childItemControl;
			}
		}
	},
	onItemClick: function(evt) {
		Event.stop(evt);
		
		if (this.childItemControls && this.childItemControls.length > 0) {
			this.isExpanded = !this.isExpanded;
			this.draw();
		}

		var selected = this.onClickCallback(this.id, this.attrs, this.isExpanded, evt);
		if (selected == true) {
			if (this.treeControl.selectedControl) {
				this.treeControl.selectedControl.setSelected(false);
			}
			this.setSelected(true);
		}
	},
	setSelected: function(bool) {
		this.isSelected = bool;
		
		if (bool) {
			this.treeControl.selectedControl = this;
			this.elmItemText.className = 'selected';	
		} else {
			this.elmItemText.className = '';
		}
	},
	draw: function() {
		if (this.elmItem) {
			this.elmItemText.update(this.getLabelCallback(this.id, this.attrs));
			this.elmItem.className = this.getElementClassNameCallback(this.id, this.attrs, this.isExpanded);
		}
		
		if (this.isExpanded) {
			if (this.childItemControls && this.childItemControls.length > 0) {
				var i = 0;
				var length = this.childItemControls.length;
				for (i = 0; i < length; i++) {
					var childItemControl = this.childItemControls[i];
					childItemControl.draw();
				}
				this.elmChildItemsRoot.show();
			}
		} else {
			if (this.elmChildItemsRoot) {
				this.elmChildItemsRoot.hide();
			}
		}
	},
	setOptions: function(options) {
		this.options = {
			data: {},
			getElementIdCallback: Prototype.emptyFunction,
			getLabelCallback: Prototype.emptyFunction,
			getElementClassNameCallback: Prototype.emptyFunction,
			onClickCallback: Prototype.emptyFunction,
			getItemActionsCallback: Prototype.emptyFunction,
			treeControl: null
		};
		Object.extend(this.options, options || {});
	}
};

var TreeControl = Class.create();
TreeControl.prototype = {
	id: null,
	itemControls: null,
	elmContainer: null,
	elmRoot: null,
	selectedControl: null,
	itemControlsMap: {},
	initialize: function(options) {
		this.setOptions(options);
		
		this.id = this.options.id;
		this.elmContainer = $(this.options.containerId);
		this.initTree();	
		this.draw();
	},
	initTree: function() {
		var data = this.options.data;
		if (data != undefined && data.length > 0) {

			this.itemControls = new Array();
			var i = 0;
			var length = data.length;

			this.elmRoot = document.createElement('UL');
			Element.extend(this.elmRoot);
			this.elmContainer.update('');
			this.elmContainer.appendChild(this.elmRoot);

			for (i = 0; i < length; i++) {
				var datum = data[i];
				var treeItem = new TreeItem({
					data: datum,
					getLabelCallback: this.options.getLabelCallback,
					getElementIdCallback: this.options.getElementIdCallback,
					getElementClassNameCallback: this.options.getElementClassNameCallback,
					onClickCallback: this.options.onClickCallback,
					getItemActionsCallback: this.options.getItemActionsCallback,
					treeControl: this
				});
				this.elmRoot.appendChild(treeItem.elmItem);
				this.itemControls[i] = treeItem;
			}
		}
	},
	draw: function() {
		if (this.itemControls && this.itemControls.length > 0) {
			var i = 0;
			var length = this.itemControls.length;
			for (i = 0; i < length; i++) {
				var itemControl = this.itemControls[i];
				itemControl.draw();
			}
		}
	},
	setOptions: function(options) {
		this.options = {
			id: '',
			data: new Array(),
			containerId: '',
			getElementIdCallback: Prototype.emptyFunction,
			getLabelCallback: Prototype.emptyFunction,
			getElementClassNameCallback: Prototype.emptyFunction,
			onClickCallback: Prototype.emptyFunction,
			getItemActionsCallback: Prototype.emptyFunction
		};
		Object.extend(this.options, options || {});
	}
};

var Message = {
	funcWindowScrollUpdateXYHandler: null,
	showMessage: function(text) {
		var messageDiv = iConnect.Commons.getPageAttribute('message.message-div');
		if (!messageDiv) {
			messageDiv = document.createElement('div');
			Element.extend(messageDiv);
			messageDiv.setStyle({
				'font-family': 'Arial, Helvetica, Verdana',
				'font-size': '12px',
				'font-weight': 'bold',
				color: 'white',
				'background-color': 'red',
				position: 'absolute',
				'z-index': 1000,
				left: 0,
				top: 0,
				padding: '2px 10px 2px 10px'
			});
			iConnect.Commons.setPageAttribute('message.message-div', messageDiv);
			var body = document.getElementsByTagName('body')[0];
			body.appendChild(messageDiv);
		}
		this.updateMessageXY();
		messageDiv.update(text);
		messageDiv.show();
		if (this.funcWindowScrollUpdateXYHandler == null) {
			this.funcWindowScrollUpdateXYHandler = this.updateMessageXY.bind(this);
		}
		Event.observe(window, 'scroll', this.funcWindowScrollUpdateXYHandler);
	},
	hideMessage: function() {
		var messageDiv = iConnect.Commons.getPageAttribute('message.message-div');
		if (messageDiv) {
			messageDiv.hide();
			Event.stopObserving(window, 'scroll', this.funcWindowScrollUpdateXYHandler);
		}
	},
	updateMessageXY: function() {
		var messageDiv = iConnect.Commons.getPageAttribute('message.message-div');
		if (messageDiv) {
			var de = document.documentElement;
			var b = document.body;
			var left = (de.scrollLeft || b.scrollLeft) - (de.clientLeft || 0);;
			var top = (de.scrollTop || b.scrollTop) - (de.clientTop || 0);
			messageDiv.setStyle({left: ( left + 'px' ), top: ( top + 'px' )});
		}
	}
};

var InPlaceEditorExt = Class.create();

InPlaceEditorExt.prototype = {
	objControl: Object,
	initialize: function(options) {
		this.setOptions(options);
		if (this.options.URL == null || this.options.URL == '' ||
			this.options.elementId == null || this.options.elementId == '') {
			return;
		}
		
		var theElement = $(this.options.elementId);
		theElement.setStyle({cursor: 'text'});
		var callbackOptions = {
			callback: function(form, value) {
				return this.options.paramName + '=' + escape(value);
			}.bind(this)
		};
		Object.extend(this.options, callbackOptions);
		this.objControl = new Ajax.InPlaceEditor(this.options.elementId, this.options.URL, this.options);
	},
	setOptions: function(options) {
		this.options = { // default options
			URL				: '',
			paramName		: 'value',
			okButton		: true,
			okText			: 'Ok',
			cancelLink		: true,
			cancelText		: 'Cancel',
			savingText		: 'Saving...',
			clickToEditText	: 'Click to edit',
			evalScripts		: true,
			elementId		: '',
			ajaxOptions		: {
				method			: 'POST',
				postBody		: '',
				asynchronous	: true
			},
			onComplete		: function(transport, element) {
				if (!transport || !transport.responseText) return;
				var json = eval('(' + transport.responseText + ')');
				if (json['result'] != 0) {
					this.options.onFailure(json);
				} else {
					this.options.onChange(json);
				}
				element.update(json['value']);
			}.bind(this),
			callback		: function(form, value) {},
			onChange		: Prototype.emptyFunction,
			onFailure		: Prototype.emptyFunction
		};
		
		Object.extend(this.options, options || {});
	},
	getText: function() {
		return this.objControl.getText();
	}
};

//------------------------------------------------------------------------------------------------------
//-------------- Interface
//------------------------------------------------------------------------------------------------------
var UI = {
	show: function(elementId) {
		var elm = $(elementId);
		if (elm && elm.show) {
			elm.show();
		}
	},
	hide: function(elementId) {
		var elm = $(elementId);
		if (elm && elm.hide) {
			elm.hide();
		}
	},
	makePopup: function(options) {
		return new Popup(options); 
	},
	showMessage: function(text) {
		Message.showMessage(text);
	},
	hideMessage: function() {
		Message.hideMessage();
	},
	enableInPlaceEditing: function(options) {
		return new InPlaceEditorExt(options);
	},
	makeTreeControl: function(options) {
		return new TreeControl(options);
	}
};