/**
 * InfoBox constructor
 * @constructor
 * @param {String} title The title of the actionbox
 * @param {String} content The content of the actionbox
 * @param {Boolean} singleInstance Set to true or false for one or multiple actionboxes at the same time
 */
function InfoBox(title, content, singleInstance)
{
	// Private
	var dom				= new DOM(),
		cursorOffsetX	= 0,
		cursorOffsetY	= 0,
		scrollBarWidth	= 30,
		timer			= null,
		timeout			= 200;

	/**
	 * Infobox state active true or false
	 * @type Boolean
	 * @field
	 */
	var active			= false;
	/**
	 * The object the Infobox is attached to
	 * @type Object
	 * @field
	 */
	var	attachedTo		= null;

	// Public
	this.setPosition	= setPosition;
	this.setTitle		= setTitle;
	this.setContent		= setContent;
	this.getAttachedTo	= getAttachedTo;
	this.show			= show;
	this.close			= close;

	if (InfoBox.instance != undefined)
	{
		InfoBox.instance.close();

		delete InfoBox.instance;
	}

	if (singleInstance == true)
	{
		InfoBox.instance = this;
	}

	// InfoBox 
	var infoBox = dom.create(
		"div",
		{
			className : "infobox"
		},
		[
			dom.create(
				"div",
				{
					className : "infobox_title"
				},
				[
					dom.create(
						"h2",
						null,
						[title]
					)
				]
			),
			dom.create(
				"div",
				{
					className : "infobox_content"
				},
				[
					dom.create(
						"div",
						{
							className : "infobox_content_wrapper"
						},
						content
					)
				]
			)
		]
	);

	// InfoBox width
	var width = Number(infoBox.getStyle("width").match(/[0-9]+/))
			  + Number(infoBox.getStyle("paddingLeft").match(/[0-9]+/))
			  + Number(infoBox.getStyle("paddingRight").match(/[0-9]+/));

	/**
	 * Set the position of the InfoBox
	 * @param {Number} positionX The position of the InfoBox on the X axis.
	 * @param {Number} positionY The position of the InfoBox on the Y axis.
	 */
	function setPosition(positionX, positionY)
	{
		var flip = false;

		// If there's no room left on the right of the cursor, position the info box on the left side
		if ((width + positionX) > (document.body.clientWidth - scrollBarWidth))
		{
			flip = true;

			infoBox.addClass("flip");
		}
		else
		{
			flip = false;

			infoBox.removeClass("flip");
		}

		// Set position
		infoBox.style.top	= positionY - 10 + "px";
		infoBox.style.left	= (flip == true ? positionX - width - 8 : positionX + 8) + "px";
	}

	/**
	 * Set the title of the InfoBox
	 * @param {String} title The title of the InfoBox as a string.
	 */
	function setTitle(title)
	{
		if (typeof title != "string") 
		{
			throw new Error("Invalid argument: title is missing or not a string");
		}

		infoBox.getFirstByTagName("h2").firstChild.nodeValue = title;
	}

	/**
	 * Set the content of the InfoBox
	 * @param {String} content The content of the InfoBox as a string.
	 * @param {Boolean} useDom By default, the content is supplied as a string. Set this parameter to true for use of DOM objects (as an array)
	 */
	function setContent(content, useDom)
	{
		if (typeof useDom != "boolean") 
		{
			useDom = false;
		}

		if (typeof content != "string" && useDom == false) 
		{
			throw new Error("Invalid argument: content is missing or not a string");
		}

		var contentWrapper = infoBox.getByClassName("infobox_content_wrapper", "div")[0];
		
		if (useDom == true)
		{
			var contentWrapperReplacement = dom.create(
				"div",
				{
					className : "infobox_content_wrapper"
				},
				content
			);
		}
		else
		{
			var contentWrapperReplacement = dom.create(
				"div",
				{
					className : "infobox_content_wrapper"
				}
			);

			contentWrapperReplacement.innerHTML = content;
		}

		contentWrapper.parentNode.replaceChild(contentWrapperReplacement, contentWrapper);
	}

	/**
	 * Get the element to which the InfoBox is attached
	 * @return {Object} The element to which the InfoBox is attached.
	 */
	function getAttachedTo()
	{
		return attachedTo;
	}

	/**
	 * Show the InfoBox
	 * @param {Object} attachTo The object the actionbox is attached to
	 */
	function show(attachTo)
	{
		if (active == false)
		{
			active		= true;
			attachedTo	= attachTo;

			attachedTo.onmouseout = function(){close()};

			// The infobox is shown after a timeout, to prevent flashing when accidentally triggering a mouseover
			timer = setTimeout(function()
				{
					dom.getFirstByTagName("body").appendChild(infoBox);
				},
				timeout
			);

			// Update the width, as some browsers (*cough* IE *cough*) need the element to be in a DOM document to able to get the computed style
			width = infoBox.offsetWidth;
		}
	}

	/**
	 * Close the InfoBox
	 * @param {Boolean} false
	 */
	function close()
	{
		if (active == true)
		{
			active		= false;
			attachedTo	= null;

			clearTimeout(timer);

			infoBox.remove();
		}

		return false;
	}
}