(function() {

	SL.ui.Tooltip = Class.create(SL.Component, {

		init : function() {

			this.config.setDefault('hide_delay', 500);
			this.config.setDefault('show_delay', 500);
			this.config.setDefault('mode', 'normal');

			this.trigger = $(this.config.get('connect_id'));

			this.followMouse = this.config.get('mode') == 'follow_mouse';

			SL.utils.reparentToBody(this.e);

			this.e.setStyle({
				display : 'block',
				position : 'absolute',
				zIndex : '999'
			});
			this.e.hide();

			this.onTriggerEnterEvent = this.onTriggerEnter.bind(this);
			this.onTriggerLeaveEvent = this.onTriggerLeave.bind(this);

			this.trigger.observe('mouseenter', this.onTriggerEnterEvent);
			this.trigger.observe('mouseleave', this.onTriggerLeaveEvent);

			this.initURL = this.config.get('url');
		},

		notifyInitEvent : function(event) {
			this.onTriggerEnter(event);
		},

		onTriggerEnter : function(event) {

			this.onTrigger = true;

			if (this.hideTimeout) {
				clearTimeout(this.hideTimeout);
				this.hideTimeout = undefined;
			}

			if (this.showTimeout) {
				return;
			}

			if (this.initURL) {
				this.load(this.initURL);
				this.initURL = undefined;
			}

			this.onTooltipEnterEvent = this.onTooltipEnter.bind(this);
			this.onTooltipLeaveEvent = this.onTooltipLeave.bind(this);

			this.e.observe('mouseenter', this.onTooltipEnterEvent);
			this.e.observe('mouseleave', this.onTooltipLeaveEvent);

			this.showTimeout = setTimeout(this._showDefered.bind(this), this.config.get('show_delay'));

			this.triggerPosition = {
				left : event.pageX,
				top : event.pageY
			};
		},

		onTriggerLeave : function(event) {

			this.onTrigger = false;

			if (this.showTimeout) {
				clearTimeout(this.showTimeout);
				this.showTimeout = undefined;
			}

			if (!this.shown) {
				return;
			}

			if (this.hideTimeout) {
				clearTimeout(this.hideTimeout);
				this.hideTimeout = undefined;
			}

			if (!this.onTooltip) {

				var delay = this.config.get('hide_delay');
				if (delay > 0) {
					this.hideTimeout = setTimeout(this._hide.bind(this), delay);
				} else {
					this._hide();
				}
			}
		},

		onTooltipEnter : function(event) {
			if (this.hideTimeout) {
				clearTimeout(this.hideTimeout);
				this.hideTimeout = undefined;
			}
			this.onTooltip = true;
		},

		onTooltipLeave : function(event) {
			this.onTooltip = false;
			if (!this.onTrigger) {
				this.timeout = setTimeout(this._hide.bind(this), this.config.get('hide_delay'));
			}
		},

		_showDefered : function() {

			this.showTimeout = undefined;

			if (!this.onTrigger && !this.onTooltip) {
				return;
			}

			if (SL.ui._staticTooltip) {
				if (!this.trigger.up('#' + SL.ui._staticTooltip.id)) {
					return;
				}
			}

			this.shown = true;
			SL.ui._registerActiveTooltip(this);

			if (this.followMouse) {
				this.onMouseMoveEvent = this.onMouseMove.bind(this);
				this.trigger.observe('mousemove', this.onMouseMoveEvent);
				this.e.observe('mousemove', this.onMouseMoveEvent);
			}

			this._update();
		},

		onSuccess : function() {
			this._update();
		},

		onChildComponentChanged : function() {
			this._update();
		},

		onMouseMove : function(event) {

			var offsets = this.trigger.cumulativeOffset();
			if (event.pageX < offsets.left) {
				return;
			}

			if (event.pageX > offsets.left + this.trigger.getWidth()) {
				return;
			}

			if (event.pageY < offsets.top) {
				return;
			}

			if (event.pageY > offsets.top + this.trigger.getHeight()) {
				return;
			}

			this.triggerPosition = {
				left : event.pageX,
				top : event.pageY
			};
			this._updatePositions();
		},

		_update : function() {
			new SL.effects.Preloader(this.e, this._updatePositions.bind(this)).run();
		},

		_updatePositions : function() {
			if (!this.shown) {
				return;
			}

			if (this.config.get('mode') == 'beside') {
				var offsets = this.trigger.cumulativeOffset();

				var left = offsets.left + this.trigger.getWidth() + 5;
				var top = offsets.top + this.trigger.getHeight() / 2 - this.e.getHeight() / 2;

				var scrollOffsets = document.viewport.getScrollOffsets();
				var viewDimensions = document.viewport.getDimensions();

				if (left + this.e.getWidth() > scrollOffsets.left + viewDimensions.width) {
					left = offsets.left - 5 - this.e.getWidth();
				}

				if (top < scrollOffsets.top) {
					top = scrollOffsets.top;
				}

				if (top + this.e.getHeight() > scrollOffsets.top + viewDimensions.height) {
					top = scrollOffsets.top + viewDimensions.height - this.e.getHeight();
				}

				this.e.setStyle({
					left : left + 'px',
					top : top + 'px'
				});

				if ((this.onTrigger || this.onTooltip) && this.shown) {
					this.e.show();
				}

				return;
			}

			if (this.config.get('mode') == 'below') {
				var offsets = this.trigger.cumulativeOffset();

				var top = offsets.top + this.trigger.getHeight() + 5;

				this.e.setStyle({
					left : offsets.left + 'px',
					top : top + 'px'
				});

				if ((this.onTrigger || this.onTooltip) && this.shown) {
					this.e.show();
				}

				return;
			}

			if (this.followMouse) {
				if (!this.triggerPosition) {
					console.log("No trigger position");
					return;
				}

				var left = this.triggerPosition.left + 2;
				var top = this.triggerPosition.top + 2;

				var scrollOffsets = document.viewport.getScrollOffsets();
				var viewDimensions = document.viewport.getDimensions();

				var rightView = scrollOffsets.left + viewDimensions.width;
				var rightDist = left + this.e.getWidth() - rightView;
				if (rightDist > 0) {
					var leftDist = scrollOffsets.left - (left - this.e.getWidth());
					if (leftDist < rightDist) {
						left = left - this.e.getWidth() - 2;
					}
				}

				var bottomView = scrollOffsets.top + viewDimensions.height;
				var bottomDist = top + this.e.getHeight() - bottomView;
				if (bottomDist > 0) {
					var topDist = scrollOffsets.top - (top - this.e.getHeight());
					if (topDist < bottomDist) {
						top = top - this.e.getHeight() - 2;
					}
				}

				this.e.setStyle({
					left : left + 'px',
					top : top + 'px'
				});

				if ((this.onTrigger || this.onTooltip) && this.shown) {
					this.e.show();
				}

				return;
			}

			var offsets = this.trigger.cumulativeOffset();

			var scrollOffsets = document.viewport.getScrollOffsets();
			var viewDimensions = document.viewport.getDimensions();

			var left;
			if (this.triggerPosition) {
				left = this.triggerPosition.left;
			} else {
				left = offsets.left + this.trigger.getWidth();
			}
			if (left + this.e.getWidth() > scrollOffsets.left + viewDimensions.width) {
				left = Math.max(offsets.left - this.e.getWidth(), scrollOffsets.left);
			}

			var top = offsets.top + this.trigger.getHeight();
			if (top + this.e.getHeight() > scrollOffsets.top + viewDimensions.height) {
				top = Math.max(offsets.top - this.e.getHeight(), scrollOffsets.top);
			}

			this.e.setStyle({
				left : left + 'px',
				top : top + 'px'
			});

			if ((this.onTrigger || this.onTooltip) && this.shown) {
				this.e.show();
			}
		},

		_hide : function(omitUnregister) {
			if (!omitUnregister && (this.onTrigger || this.onTooltip)) {
				return;
			}
			if (this.hideTimeout) {
				clearTimeout(this.hideTimeout);
				this.hideTimeout = undefined;
			}
			if (this.showTimeout) {
				clearTimeout(this.showTimeout);
				this.showTimeout = undefined;
			}
			this.e.hide();
			this.shown = false;

			if (this.onTooltipEnterEvent) {
				this.e.stopObserving('mouseenter', this.onTooltipEnterEvent);
				this.e.stopObserving('mouseleave', this.onTooltipLeaveEvent);
			}

			if (this.onMouseMoveEvent) {
				this.trigger.stopObserving('mousemove', this.onMouseMoveEvent);
				this.e.stopObserving('mousemove', this.onMouseMoveEvent);
			}

			this.onTrigger = false;
			this.onTooltip = false;
			this.triggerPosition = undefined;

			if (!omitUnregister) {
				SL.ui._registerActiveTooltip(null);
			}
		},

		cleanup : function() {
			this.trigger.stopObserving('mouseenter', this.onTriggerEnterEvent);
			this.trigger.stopObserving('mouseleave', this.onTriggerLeaveEvent);

			if (this.onTooltipEnterEvent) {
				this.e.stopObserving('mouseenter', this.onTooltipEnterEvent);
				this.e.stopObserving('mouseleave', this.onTooltipLeaveEvent);
			}

			this.e.hide();
			this.e.remove();
		}
	});

	SL.ui.Bubble = Class.create(SL.Component, {

		init : function() {

			this.attached = $(this.config.get('attach_id'));
			this.position = this.config.get('position');

			this.isStatic = this.config.get('static');

			SL.utils.reparentToBody(this.e);

			this.e.setStyle({
				display : 'block',
				position : 'absolute',
				zIndex : '999'
			});

			this.e.hide();

			if (this.isStatic) {
				this.show();
			} else {
				this.trigger = this.attached.down('input');
				if (this.trigger) {
					this.trigger.observe('focus', this.show.bind(this));
					this.trigger.observe('blur', this.hide.bind(this));

					if (document.activeElement == this.trigger) {
						this.show();

					} else if (this.config.get('visible')) {
						this.trigger.focus();
					}
				}
				if (this.config.get('visible')) {
					this.show();
				}
			}

			this.connectDestroy(this.attached);
		},

		show : function() {
			if (!this.shown) {
				this.shown = true;
				this._update();
			}
		},

		hide : function() {
			if (!this.isStatic && this.shown) {
				this.e.hide();
				this.shown = false;
			}
		},

		_update : function() {
			new SL.effects.Preloader(this.e, this._updatePositions.bind(this)).run();
		},

		_updatePositions : function() {
			if (!this.shown) {
				return;
			}

			var aWidth = this.attached.getWidth();
			var aHeight = this.attached.getHeight();

			var offsets = this.attached.cumulativeOffset();
			var tag = this.attached.tagName.toLowerCase();
			if (tag == 'td' || tag == 'th') {
				var tr = this.attached.up('tr');
				offsets.top = tr.cumulativeOffset().top;
				aHeight = tr.getHeight();
			}

			var top;
			var left;

			if (this.position == 'top') {
				left = offsets.left + aWidth / 2 - this.e.getWidth() / 2;
				top = offsets.top - this.e.getHeight() - 5;
			} else if (this.position.startsWith('bottom')) {
				top = offsets.top + aHeight + 5;
				if (this.position.endsWith('_left')) {
					left = offsets.left;
				} else {
					left = offsets.left + aWidth / 2 - this.e.getWidth() / 2;
				}
			} else {
				top = offsets.top + aHeight / 2 - this.e.getHeight() / 2;
				if (this.position == 'left') {
					left = offsets.left - this.e.getWidth() - 5;
				} else {
					left = offsets.left + aWidth + 5;
				}
			}

			this.e.setStyle({
				left : left + 'px',
				top : top + 'px'
			});

			this.e.show();
			setTimeout(this._updatePositions.bind(this), 250);
		}

	});

	SL.ui._registerActiveTooltip = function(tt) {
		if (SL.ui._activeTooltip) {
			if (SL.ui._activeTooltip == tt) {
				return;
			}
			SL.ui._activeTooltip._hide(true);
		}
		SL.ui._activeTooltip = tt;
	};

	SL.ui._closeActiveTooltips = function() {
		if (SL.ui._activeTooltip) {
			try {
				SL.ui._activeTooltip._hide(true);
			} catch (e) {
			}
		}
	};

	SL.ui._hasStaticTooltip = function(tt) {
		return SL.ui._staticTooltip ? true : false;
	};

	SL.ui._registerStaticTooltip = function(tt) {
		if (SL.ui._staticTooltip) {
			if (SL.ui._staticTooltip == tt) {
				return;
			}
			SL.ui._staticTooltip._hide(true);
		}
		SL.ui._staticTooltip = tt;
	};

	SL.ui._closeStaticTooltips = function() {
		if (SL.ui._staticTooltip) {
			try {
				SL.ui._staticTooltip._hide(true);
			} catch (e) {
			}
		}
	};

})();

