(function($){
	
	$.fn.islink_tooltip = function(options) {
		var options = $.extend({
			buttons: {},
			forcePosition: false,
			overlayPadding: 5,
			fadeInSpeed: 100,
			fadeOutSpeed: 100,
			loadingText: '<img src="/img/popup_ajax_loader.gif" />',
			onClick: false
		}, options);

		var curOverlay = null;
		var overlink 	= false;
		var overOverlay = false;
		var linkObject 	= null;
		var viewPort = null;
		var uuid = -1;
		var linkObjectAnchor  = {};
		var position = "";
		var allowedPositions = ['top','right','bottom','left'];
		var overlayObject = {container:null, arrow: null};
		
		/** Main function
		 *
		 */
		function showOverlay()
		{
			linkObject.attr('href','javascript:void(0)');
			
			calculateAnchors();
			
			if(!options.forcePosition)
				position = calculateBestPosition();
			else
				position = getForcedPosition();
				
			viewPort = getViewPortArea();
				
			drawArrow();
			drawOverlay();
			
			var href = linkObject.attr('data-overlay');
			if(typeof href == 'undefined' || href == "")
				href = "No data to display...";
				
			// Assumption: Links start with "/"
			if(href.indexOf('/') == 0)
			{
				$(overlayObject.content).load(href, function(a,b,c) { 
					if(b == "error")
					{
						$(this).html('Sorry, but an error occured. The URL could not be loaded... Remember: AJAX-calls do not work cross domain!');
					}
					
					redrawOverlay();
				});
			}
			else
			{
				$(overlayObject.content).html(href);
				redrawOverlay();
				
			}
			
			showAll();
		}
		
		function showAll()
		{
			window.setTimeout(function(){
				overlayObject.container.fadeIn(options.fadeInSpeed);
				overlayObject.arrow.fadeIn(options.fadeInSpeed);
			}, 150);
		}
		
		function drawOverlay()
		{
			if(overlayObject.arrow.length == 0)
				return;
				
			overlayObject.container = $('<div>').addClass('ui-overlay ui-overlay-content-container').addClass(uuid);
			overlayObject.container.bind({
				mouseover: function() {
					overOverlay = true;
					overLink = false;
				},
				mouseout: function() {
					overOverlay = false;
					removeOverlay();	
				}
			});
			
			overlayObject.content = $('<div>').addClass('ui-overlay-content').html(options.loadingText);
			overlayObject.container.append(overlayObject.content);
			
			overlayObject.buttons = $('<div>').addClass('ui-overlay-buttons');
			overlayObject.container.append(overlayObject.buttons);
			
			$('body').append(overlayObject.container);
			
			var pos = calculateOverlayPosition();
			overlayObject.container.css({
				top: pos.top,
				left: pos.left
			});
		}
		
		function redrawOverlay()
		{
			var pos = calculateOverlayPosition();
			overlayObject.container.css({
				top: pos.top,
				left: pos.left
			});
		}
		
		function calculateOverlayPosition()
		{
			var anchor = getAnchorByPosition();
			var top = anchor.top;
			var left = anchor.left;
			
			switch(position)
			{
				case 'top':
					top 	-= overlayObject.arrow.height() + options.overlayPadding + overlayObject.container.outerHeight() - 1;
					left 	-= overlayObject.arrow.width()/2 + 10;
					break;
				case 'right':
					top		-= overlayObject.arrow.height()/2 + 10;
					left    += options.overlayPadding + overlayObject.arrow.width() - 1;
					break;
				case 'bottom':
					left	-= overlayObject.arrow.width()/2 + 10;
					top		+= options.overlayPadding + overlayObject.arrow.height() - 1;
					
					if(left + overlayObject.container.outerWidth(true) > viewPort.right) {
						left -= (left + overlayObject.container.outerWidth(true)) - viewPort.right + options.overlayPadding*2;
					}
						
					break;
				case 'left':
					top		-= overlayObject.arrow.height()/2 + 10;
					left	-= overlayObject.arrow.width() + options.overlayPadding + overlayObject.container.width() + 1;
			}

			return {top: top, left: left};
		}
		
		/**
		 * This method is about to display the arrow indicating to which element
		 * the overlay belongs to.
		 */
		function drawArrow()
		{
			overlayObject.arrow = $('<div>').addClass('ui-overlay ui-overlay-arrow').addClass(position).addClass(uuid);
			$('body').append(overlayObject.arrow);
			var anchor = getAnchorByPosition();
			var top = anchor.top;
			var left = anchor.left;
			
			switch(position)
			{
				case 'top':
					top 	-= overlayObject.arrow.height() + options.overlayPadding;
					left 	-= overlayObject.arrow.width()/2;
					break;
				case 'right':
					top		-= overlayObject.arrow.height()/2;
					left    += options.overlayPadding;
					
					break;
				case 'bottom':
					left	-= overlayObject.arrow.width()/2;
					top		+= options.overlayPadding;
					break;
				case 'left':
					top		-= overlayObject.arrow.height()/2;
					left	-= overlayObject.arrow.width() + options.overlayPadding;
			}

			overlayObject.arrow.css({
				top: top,
				left: left
			});
		}
		
		/**
		 * Since a window has limited space, this method calculates where to draw the overlay.
		 * It's done by calculating the distances to all four borders of the browser (including
		 * scrolling).
		 */
		function calculateBestPosition()
		{
			if(linkObjectAnchor == null)
				calculateAnchors();
				
			var left = linkObjectAnchor.left.left - $(window).scrollLeft();
			var top	 = linkObjectAnchor.top.top - $(window).scrollTop();
			var right = $(window).width() - $(window).scrollLeft() - linkObjectAnchor.right.left;
			var bottom = $(window).height() - $(window).scrollTop() - linkObjectAnchor.bottom.top;
			
			if(left > 400) 	 left 	= 999999;
			if(top > 400) 	 top 	= 999999;
			if(right > 400)  right	= 999999;
			if(bottom > 400) bottom = 999999;
			
			var pos = Math.max(top, left, bottom, right);
			
			/* */
			if(pos == top)		return "top";
			if(pos == left) 	return "left";
			if(pos == bottom)	return "bottom";
			if(pos == right)	return "right";
		}
		
		/**
		 * calculates all necessary anchors of an element: top, left, bottom, right and center
		 */
		function calculateAnchors()
		{
			linkObjectAnchor = {
				top: top(),
				right: right(),
				bottom: bottom(),
				left: left(),
				center: center()
			};
		}
		
		/**
		 * Checks wether a given "forced-position" is allowed, otherwise falls back
		 * to calculating the best position :)
		 */
		function getForcedPosition()
		{
			for(i in allowedPositions)
			{
				if(allowedPositions[i] == options.forcePosition)
					return options.forcePosition; 
			}
			
			return calculateBestPosition();
		}
		
		/**
		 * Getter of the anchor field
		 * 
		 * @return Anchor with top/left coordinates, the left one by default 
		 */
		function getAnchorByPosition()
		{
			if(linkObjectAnchor == null)
				calculateAnchors();
				
			switch(position)
			{
				case 'top':
					return linkObjectAnchor.top;
					break;
				case 'right':
					return linkObjectAnchor.right;
					break;
				case 'bottom':
					return linkObjectAnchor.bottom;
					break;
				case 'left':
				default:
					return linkObjectAnchor.left;
			}
		}
		
		/**
		 * Method to calculate the top anchor
		 */
		function top()
		{
			leftPos = linkObject.offset().left + linkObject.outerWidth()/2;
			topPos = linkObject.offset().top;
			return {left: Math.round(leftPos), top: Math.round(topPos)};
		}
		
		/**
		 * Method to calculate the left anchor
		 */
		function left()
		{
			leftPos = linkObject.offset().left;
		 	topPos = linkObject.offset().top + linkObject.outerHeight()/2;
			return {left: Math.round(leftPos), top: Math.round(topPos)};
		}
		
		/**
		 * Method to calculate the right anchor
		 */
		function right()
		{
			leftPos = linkObject.offset().left + linkObject.outerWidth() - 1;
			topPos = linkObject.offset().top + Math.round(linkObject.outerHeight()/2);
			return {left: Math.round(leftPos), top: Math.round(topPos)};
		}
		
		/**
		 * Method to calculate the bottom anchor
		 */
		function bottom()
		{
			leftPos = linkObject.offset().left + Math.round(linkObject.outerWidth()/2);
			topPos = linkObject.offset().top + linkObject.outerHeight() - 1;
			return {left: Math.round(leftPos), top: Math.round(topPos)};
		}
		
		/**
		 * Method to calculate the center anchor
		 */
		function center()
		{
			leftPos = linkObject.offset().left + Math.round(linkObject.outerWidth()/2);
			topPos = linkObject.offset().top + linkObject.outerHeight()/2;
			return {left: Math.round(leftPos), top: Math.round(topPos)};
		}
		
		/**
		 * Method to remove all overlay elements
		 */
		function removeOverlay(fork)
		{
			$('.ui-overlay').each(function(){
				if(!$(this).hasClass(uuid))
					$(this).remove();
			});

			window.setTimeout(function() { 
				
				if(!overLink && !overOverlay)
				{
					$('.ui-overlay').fadeOut(options.fadeOutSpeed, function(){
						curOverlay = null;
						uuid = -1;
						$('.ui-overlay').remove();
					});
				}
					
			}, 50);
		}
		
		function getGUID() {
		    var S4 = function() {
		       return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
		    };
		    return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
		}

		function getViewPortArea()
		{
			return {
				top:  $(window).scrollTop(),
				right: $(window).width() + $(window).scrollLeft(),
				bottom: $(window).height() + $(window).scrollTop(),
				left: $(window).scrollLeft(),
			};
		}
		
		// Append UUIDs to each link with overlay
		$(this).each(function(){
			$(this).data('uuid', getGUID());	
		});
		
		$(this).bind({
			mouseenter: function() {
				if(!$(this).hasClass(uuid))
				{
					curOverlay = this;
					linkObject = $(this);
					uuid = linkObject.data('uuid');
					$(this).addClass(uuid); 
					removeOverlay(true);
					showOverlay();
				}
				overLink = true;
			},
			mouseout: function() { 
				overLink = false;
				removeOverlay(); 
			}
		});
	};

})(jQuery);
