if(!Richfaces.tooltips){
	Richfaces.tooltips = [];
}

ToolTip = Class.create();

ToolTip.prototype = {
	initialize:function(events, id, parentId, mode, disabled, direction, followMouse, horizontalOffset, verticalOffset, ajaxFunction, ajaxOptions){
		this.event = events.event;
		this.onshow = new Function('event', events.onshow + ';return true;');
		this.oncomplete = new Function('event', events.oncomplete + ';return true;');
		this.onhide =  new Function('event', events.onhide + ';return true;');
		this.delay = events.delay;
		
		this.id = id;
		this.parentId = parentId;
		this.mode = mode;
		this.direction = direction;
		this.disabled = disabled;
		this.followMouse = followMouse;
		this.horizontalOffset = horizontalOffset;
		this.verticalOffset = verticalOffset;
		
		this.ajaxExecuteString = ajaxFunction;
		this.ajaxOptions = ajaxOptions;
		this.clientAjaxParams = {};
		
		this.toolTip = $(id);
		
		this.toolTipContent = $(id + 'content');
		this.toolTipDefaultContent = $(id + 'defaultContent');
		
		this.toolTip.style.visibility='hidden';
		this.toolTip.style.display='block';
		
		this.toolTipOffsetW = this.toolTip.offsetWidth;
		this.toolTipOffsetH = this.toolTip.offsetHeight;

		this.toolTipW = this.toolTip.getWidth();
		this.toolTipH = this.toolTip.getHeight();
		
		this.toolTipBorderHeight = (this.toolTipOffsetH - this.toolTipH) / 2;  
		this.toolTipBorderWidth = (this.toolTipOffsetW - this.toolTipW) / 2;
		this.toolTip.style.visibility = 'visible';
		this.toolTip.style.display = "none";
		this.parentAttached = false;
		this.hintParentElement = null;
		
		// mouse could be overed while ajax request is still in process,
		// so, after DOM-element replaced, we should not display it
		this.isMouseOvered = false;
		
		if(Richfaces.browser.isIE6){
			var toolTipZindex = parseInt(this.toolTip.style.zIndex);
			new Insertion.Before(this.toolTip,
			"<iframe src=\"javascript:''\" frameborder=\"0\" scrolling=\"no\" id=\"" + this.id + "iframe\"" +
				"style=\"position: absolute; width: " + this.toolTipOffsetW + "px; height: " + this.toolTipOffsetH + "px; display: none;background-color:black; z-index: " + 
				+ (toolTipZindex-1) + ";\">" + "</iframe>");
			this.iframe = $(this.id + 'iframe');	
		}		
		
		this.attachOnLoadEventsListner = this.attachOnLoadEvents.bindAsEventListener(this);
		this.setToolTipPositionListner = this.setToolTipPosition.bindAsEventListener(this);
		this.doHideListner = this.doHide.bindAsEventListener(this);
		this.leaveToolTipListner = this.leaveToolTip.bindAsEventListener(this);
		
		if (Richfaces.tooltips[parentId])
		{
			Richfaces.tooltips[parentId].destroy();
		}

		if(!this.disabled)	Event.observe(document, "mousemove", this.attachOnLoadEventsListner, true);
		
		//it means we have only one tooltip for element
		Richfaces.tooltips[parentId] = this;
		
	},
	
	destroy: function()
	{
			if (!this.parentAttached) Event.stopObserving(document, "mousemove", this.attachOnLoadEventsListner, true);
			else if (this.followMouse) Event.stopObserving(this.parent, 'mousemove', this.setToolTipPositionListner, false);
						
			Event.stopObserving(this.parent, this.event, this.customEventHandlerListner, false);
			Event.stopObserving(this.parent, "mouseout", this.doHideListner, false);
			Event.stopObserving(this.toolTip, 'mouseout', this.leaveToolTipListner, false);

			this.hintParentElement = null;
			this.parent = null;
			this.toolTip = null;
			this.toolTipContent = null;
			this.toolTipDefaultContent = null;
			this.iframe = null;
			this.eventCopy = null;
			this.event = null;
	},
	
	attachOnLoadEvents: function(){
		if(!this.parentAttached){
			this.parent = $(this.parentId);
			if(this.parent != null && !this.disabled){
				this.attachParentEvents();
				this.parentAttached = true;
			}
			Event.stopObserving(document, "mousemove", this.attachOnLoadEventsListner, true);
		}
	},
	
	customEventHandler: function(event) {
		if (this.activationTimerHandle) {
			return ;
		}
		
		this.eventCopy = A4J.AJAX.CloneObject(event, false);
		
		this.activationTimerHandle = setTimeout(function() {
			this.doShow(this.eventCopy);
		}.bind(this), this.delay);
	},
	
	attachParentEvents: function(){
		if(this.followMouse){
			Event.observe(this.parent, 'mousemove', this.setToolTipPositionListner, false);	
		}
		
		var eventHandler;
		if (this.delay > 0) {
			this.customEventHandlerListner = this.customEventHandler.bindAsEventListener(this);
		} else {
			this.customEventHandlerListner = this.doShow.bindAsEventListener(this);
		}

		Event.observe(this.parent, this.event, this.customEventHandlerListner, false);
		Event.observe(this.parent, "mouseout", this.doHideListner, false);
		Event.observe(this.toolTip, 'mouseout', this.leaveToolTipListner, false);
	},
	
	detectAncestorNode: function(leaf, element) {
		// Return true if "element" is "leaf" or one of its parents
		var node = leaf;
		while (node != null && node != element)
			node = node.parentNode;
		return (node != null);
	},
	
	
	leaveToolTip: function(e) {
		
		var hintNotLeft = false;

		// detect mouse move from hint to owner
		// if mouse entered the just the owner hintNotLeft is set true
		hintNotLeft = this.detectAncestorNode(e.toElement,this.hintParentElement);
		hintNotLeft = hintNotLeft || this.detectAncestorNode(e.relatedTarget,this.hintParentElement);

		if (!hintNotLeft){
			this.doHide(e);
			this.isMouseOvered = false;
		}
	},
	
	doShow: function(e){
		
		var needToShow = true;
		var obj;
		if (!e) var e = window.event;
		var relTarg = e.relatedTarget || e.fromElement;

		while(relTarg){
			if(relTarg == this.parent){
				needToShow = false;
				break;
			}
			relTarg = relTarg.parentNode;
		}
		
		if(!needToShow) return;


		this.onshow(e);
		
		this.isMouseOvered = true;
		if (e.target)
			this.hintParentElement = e.target;
		if (e.srcElement)
			this.hintParentElement = e.srcElement;


		if(this.mode == 'ajax'){
			if(this.toolTipDefaultContent){
				this.toolTipContent.innerHTML = this.toolTipDefaultContent.innerHTML;
				
				this.toolTip.style.visibility = "hidden";
				this.toolTip.style.display = 'block';
				
				this.setToolTipPosition(e); 
				this.setToolTipVisible(false);
				
			}
			var event = e;
			var ajaxOptions = this.ajaxOptions;
			if(this.clientAjaxParams){
				if(e.clientX){
					this.clientAjaxParams['clientX'] = e.clientX;
					this.clientAjaxParams['clientY'] = e.clientY;	
				} else {
					this.clientAjaxParams['event.pageX'] = e.pageX;
					this.clientAjaxParams['event.pageY'] = e.pageY;	
					
				}
				
				Object.extend(ajaxOptions['parameters'], this.clientAjaxParams);
			}
			eval(this.ajaxExecuteString);
		} else {
			this.setToolTipPosition(e);
			this.displayDiv();
		}
		
//		if(this.delay > 0){
//			setTimeout('Richfaces.tooltips[\'' + this.parentId + '\'].displayDiv()', this.delay);	
//		} else {
//			this.displayDiv();
//		}
//		
	},

	doHide: function(e){
		this.eventCopy = null;

		var needToHide = true;

		if (!e) var e = window.event;
		var relTarg = e.relatedTarget || e.toElement;
		
		while(relTarg){
			if(relTarg == this.parent){
				needToHide = false;
				break;
			}
			relTarg = relTarg.parentNode;
		}
	
		
		if(!needToHide) return;
		
		if (this.activationTimerHandle) {
			clearTimeout(this.activationTimerHandle);
			this.activationTimerHandle = undefined;
		}
		
		var fakeEvent = false;
		fakeEvent = this.detectAncestorNode(e.toElement,this.toolTip);
		fakeEvent = fakeEvent || this.detectAncestorNode(e.relatedTarget,this.toolTip);

		
		if (!fakeEvent) {
			this.isMouseOvered = false;
			this.toolTip.style.visibility = "hidden";
			this.toolTip.style.display = "none";
			
			if(this.iframe){
				this.iframe.style.display = "none";
			}
			this.hintParentElement = null;
			this.isMouseOvered = false;
			this.onhide(e);
		}
	},
	
	doEnable: function(){
		this.disabled = false;
	},
	doDisable: function(){
		this.disabled = true;
	},
	
	/*
	 * we can pass here not event only, but also object {'clientX':XXX, 'clientY':XXX}
	 */
	setToolTipPosition: function(e){

		var toolTipX=0;
		var toolTipY=0;
		
		var x = Event.pointerX(e);
		var y = Event.pointerY(e);
//		
//		var toolTipDim = Element.getDimensions(this.toolTip);
//		this.toolTipW = toolTipDim.width;
//		this.toolTipH = toolTipDim.height;
	
		var elementDim = Richfaces.Position.getOffsetDimensions(this.toolTip);
		
		var offsets = Position.cumulativeOffset(this.toolTip);
		
		offsets[0] -= this.toolTip.offsetLeft || 0;
		offsets[1] -= this.toolTip.offsetTop || 0;
	
		toolTipX = x - offsets[0];
		toolTipY = y - offsets[1];
		
//		var windowDim = this.windowSize();
		
		var regExpression = /^(top|bottom)-(left|right)$/;
		var match = this.direction.match(regExpression);
		
		var horizontalDirection = match[2];
		var verticalDirection = match[1];
		
		this.prePosition({'x':toolTipX, 'y':toolTipY}, elementDim, horizontalDirection, verticalDirection, {'x':this.horizontalOffset, 'y':this.verticalOffset});
		
		var coords = this.fitToolTip(e.clientX, e.clientY, {'x':toolTipX , 'y':toolTipY},elementDim, horizontalDirection, verticalDirection, {'x':this.horizontalOffset, 'y':this.verticalOffset});
		
		Element.setStyle(this.toolTip, {"left": coords.x + "px", "top": coords.y + "px"});
		if(this.iframe)
		{
			this.iframe.style.top = (coords.y - this.toolTipBorderHeight) + 'px';
			this.iframe.style.left = (coords.x - this.toolTipBorderWidth) + 'px';
		}
		
		this.eventCopy = A4J.AJAX.CloneObject(e, false);
	},
	
	prePosition: function(basePoint, elementDim, horizontalDirection, verticalDirection, offset){
		var returnX, returnY;
		returnX = horizontalDirection=='left' ?  basePoint.x - elementDim.width - offset.x : basePoint.x +=  offset.x; 
		returnY = verticalDirection == 'top' ? basePoint.y - elementDim.height - offset.y : basePoint.y + offset.y;
		return {'x':returnX, 'y':returnY};
	},
	
	fitToolTip: function(clientX, clientY,basePoint,elementDim, horizontalDirection, verticalDirection, offset){
			var winDim = Richfaces.Position.getWindowDimensions();
			var deltaLeft = clientX - offset.x - elementDim.width;
			var deltaRight = winDim.width - (clientX + offset.x + elementDim.width);
			var deltaTop = clientY - offset.y - elementDim.height;
			var deltaBottom = winDim.height - (clientY + offset.y + elementDim.height);
			
			if(deltaLeft < 0){
				// in this case new direction will be right
				this.prePosition({'x':basePoint.x , 'y':basePoint.y},elementDim, 'right', verticalDirection, offset);
				var newDeltaRight = winDim.width - (clientX + offset.x + elementDim.width);
				if(newDeltaRight > 0){
					horizontalDirection = 'right';
				} else {
					if(newDeltaRight > deltaLeft){
						horizontalDirection = 'right';
					}
				}
			} else if(deltaRight < 0){
				// in this case new direction will be left
				this.prePosition({'x':basePoint.x , 'y':basePoint.y},elementDim, 'left', verticalDirection, offset);
				var newDeltaLeft = clientX - offset.x - elementDim.width;
				if(newDeltaLeft > 0){
					horizontalDirection = 'left';
				} else {
					if(newDeltaLeft > deltaRight){
						horizontalDirection = 'left';
					}
				}
			}
			
			if(deltaTop < 0){
				// in this case new direction will be right
				this.prePosition({'x':basePoint.x , 'y':basePoint.y},elementDim, horizontalDirection, 'bottom', offset);
				var newDeltaBottom = winDim.height - (clientY + offset.y + elementDim.height);
				if(newDeltaBottom > 0){
					verticalDirection = 'bottom';
				} else {
					if(newDeltaBottom > deltaTop){
						verticalDirection = 'bottom';
					}
				}				
			} else if(deltaBottom < 0){
				this.prePosition({'x':basePoint.x , 'y':basePoint.y},elementDim, horizontalDirection, 'top', offset);
				var newDeltaTop = clientY - offset.y - elementDim.height;
				if(newDeltaTop > 0){
					verticalDirection = 'top';
				} else {
					if(newDeltaTop > deltaBottom){
						verticalDirection = 'top';
					}
				}			
			}
			var coords = this.prePosition({'x':basePoint.x , 'y':basePoint.y},elementDim, horizontalDirection, verticalDirection, offset);	
			return coords;
	},
	
	displayDiv: function(){
		//this.toolTip.style.display = 'block';
		if(this.isMouseOvered){
			
			if(this.mode == 'ajax'){
				this.toolTip.style.display = 'none';
				if(this.clientAjaxParams){
					/*
					if(this.toolTipDefaultContent){
						this.toolTip.style.visibility = "hidden";
						this.toolTip.style.display = "block";
					}
					*/
					var xVarName;
					if(this.clientAjaxParams.clientX){
						xVarName = 'clientX';
					} else {
						xVarName = 'pageX';
					}
					// = this.clientAjaxParams.clientX || this.clientAjaxParams.pageX;
					var yVarName;
					if(this.clientAjaxParams.clientY) {
						yVarName = 'clientY';
					} else {
						yVarName = 'pageY';
					}
					var obj = {};
					obj[xVarName] = this.clientAjaxParams[xVarName];
					obj[yVarName] = this.clientAjaxParams[yVarName];
					//var xValue = this.clientAjaxParams[xVarName];
					//var yValue = this.clientAjaxParams[yVarName];
					this.toolTip.style.visibility = "hidden";
					this.toolTip.style.display = 'block';

					this.setToolTipPosition((this.eventCopy ? this.eventCopy : obj));
				}
			}

			this.setToolTipVisible(true);

//			if(this.delay > 0) {
//				this.queuedToolTip = setTimeout('Richfaces.tooltips[\'' + this.parentId + '\'].setToolTipVisible(true)', this.delay);	
//			} else {
//				this.setToolTipVisible(true);
//			}

			
//			if(this.mode != 'ajax'){
//				this.oncomplete(window.event);
//			}
		}
	},
	
	setToolTipVisible: function(runOnComplete){
		this.activationTimerHandle = undefined;
		this.toolTip.style.display = "block";
		this.toolTip.style.visibility = "visible";
		if(this.iframe)
		{
			this.iframe.style.display = "block";
		} 
		
		if(runOnComplete){
			this.oncomplete(window.event);	
		}
		
	}
} 