/**
 * @description Provide facility to overlay HTML on screen
 * @usage See end for sample usage
 */

nikefootball.global.Overlay = function (params) {
	var _overlayInserted = false;
	var _contentContainerInserted = false;
	var _initialised = false;

	var _overlayContentIndentTop = 92;
	var _overlayContentIndentLeft = 0;

	var _objects = {
		overlayObj					: null,
		contentContainerObj			: null
	};
	var _config = {
		objectToOverlay				: null,
		content						: null,
		opacity						: 0.8,
		overlayClassName			: 'overlay',
		contentContainerClassName	: 'overlay-content-container',
		overlayCloseClassName		: 'overlay-close',
		overlayOffset				: {
			top 	: null,
			left	: null
		},
		overlayWidth				: null,
		overlayHeight				: null,
		contentContainerTop			: 0,
		contentContainerOffset		: {
			top 	: null,
			left	: null
		},
		fadeInParams				: "fast",
		fadeOutParams				: "fast"
	};

	// PRIVATE METHODS

	/**
	 * @description Initialise the parameters
	 * @param params Object [optional] List of parameters for initialisation
	 *		objectToOverlay {DomObject}
	 *			the DOM object that needs to be overlayed
	 *		content {String}
	 *			the content to display within the overlay
	 *			NOTE: *MUST* be initialised with a non-blank value - 
	 *				so even if the initial value is blank, specify a space
	 *		opacity {Number} [optional default=_config.opacity]
	 *			the opacity to set for the overlay
	 *		activate {Boolean} [default=false]
	 *			activate the overlay immediately
	 *		overlayClassName {String} [optional default=_config.overlayClassName]
	 *			the CSS class to be applied to the overlay
	 *		contentContainerClassName {String} [optional default=_config.contentContainerClassName]
	 *			the CSS class to be applied to the content within the overlay
	 *		overlayCloseClassName {String} [optional default=_config.overlayCloseClassName]
	 *			the CSS class that will be used to close the overlay
	 *			this needs to be defined in the content
	 *		contentContainerTop {Number} [optional default=_config.contentContainerTop]
	 *			the vertical distance between the top of the overlay and the top of the content
	 *		contentContainerOffset {Number} [optional default=_config.contentContainerOffset]
	 *			the absolute offset of the content; calculated if not specified
	 *		fadeInParams {Object} [optional default=_config.fadeInParams]
	 *			default parameters for all calls to fadeIn()
	 *		fadeOutParams {Object} [optional default=_config.fadeOutParams]
	 *			default parameters for all calls to fadeOut()
	 * @returns void
	 */
	function _initParams(params) {
		if (params === undefined) {
			return;
		}
		JQ.extend(_config, params);
	};

	function _initOverlay() {
		_createOverlayNode();
		_insertOverlayNode();
		_positionOverlayNode();
	};

	function _initContentContainer() {
		_createContentContainerNode();
		_setContent();
		_insertContentContainerNode();
		_positionContentContainerNode();
		_attachContentContainerCloseHandlers();
	};

	/**
	 * @description Initialise the DOM objects: the overlay & content nodes
	 */
	function _initDom() {
		_initOverlay();
		_initContentContainer();
	};

	/**
	 * @description Calculate the dimensions of an element.
	 * @note This does NOT take into account any padding
	 *       ie, we assume that there is no padding on the object
	 */
	function _calculateElementDimensions(ele) {
		var dimensions = {
			width	: null,
			height	: null,
			offset	: null
		};
		if (!ele || (ele === undefined)) {
			return dimensions;
		}
		dimensions.width = ele.width();
		dimensions.height = ele.height();
//		dimensions.offset = ele.offset();
		dimensions.offset = ele.position();
		return dimensions;
	};

	/**
	 * @code
	 *		<div class="_config.overlayClassName">
	 *		</div>
	 */
	function _createOverlayObj() {
		return JQ('<div></div>')
			.addClass(_config.overlayClassName)
		;
	};

	function _calculateContentWidth() {
		if (_objects.contentContainerObj === null) {
			return undefined;
		}
		return _objects.contentContainerObj.children().width();
	};

	function _setContent() {
		if (_objects.contentContainerObj === null) {
			return;
		}
		if (_config.content === null || _config.content === '') {
			_config.content = ' ';
		}
		_objects.contentContainerObj.html(_config.content).hide(0);
	};

	/**
	 * @description Create the overlay content object
	 * @code
	 *		<div class="_config.contentContainerClassName">
	 *			<!-- content -->
	 *		</div>
	 */
	function _createContentContainerObj() {
		var contentContainerObj = JQ('<div></div>')
			.addClass(_config.contentContainerClassName)
		;
		return contentContainerObj; 
	};
	
	function _setOverlayNodeDimensions() {
		var overlayDimensions = _calculateElementDimensions(_config.objectToOverlay);
		_config.overlayWidth =  overlayDimensions.width || _config.overlayWidth;
		_config.overlayHeight = overlayDimensions.height || _config.overlayHeight;
		_config.overlayOffset.left = (_config.overlayOffset.left === null) ? overlayDimensions.offset.left : _config.overlayOffset.left;
		_config.overlayOffset.top = (_config.overlayOffset.top === null) ? overlayDimensions.offset.top : _config.overlayOffset.top;
		// IE hack
		if (JQ.browser.msie) {
			_config.overlayOffset.left = _overlayContentIndentLeft;
		}
	};

	function _setContentContainerNodeDimensions() {
		_config.contentContainerOffset.top = _config.overlayOffset.top + _config.contentContainerTop;
		_config.contentContainerOffset.top += _overlayContentIndentTop;
		// assume content is to be positioned in the middle
		var contentContainerWidth = _calculateContentWidth();
		_config.contentContainerOffset.left = (_config.overlayWidth - contentContainerWidth) / 2;
		_config.contentContainerOffset.left += _overlayContentIndentLeft;
	};

	function _createOverlayNode() {
		_objects.overlayObj = _createOverlayObj().hide(0);
	};

	function _createContentContainerNode() {
		if (_objects.contentContainerObj !== null) {
			return;
		}
		_objects.contentContainerObj = _createContentContainerObj().hide(0);
	};

	/**
	 * @description Insert the overlay node.
	 *		This will be the next sibling of the element that is being overlayed.
	 *		If the node has already been inserted, it won't be inserted again.
	 */
	function _insertOverlayNode() {
		if (_overlayInserted) {
			//return;
		}
		if (_config.objectToOverlay === null) {
			return;
		}
		if (_objects.overlayObj === null) {
			return;
		}
		_config.objectToOverlay.after(_objects.overlayObj);
		_setOverlayNodeDimensions();
		_overlayInserted = true;
	};

	function _insertContentContainerNode() {
		if (_contentContainerInserted) {
			return;
		}
		if (_config.objectToOverlay === null) {
			return;
		}
		if (_objects.contentContainerObj === null) {
			return;
		}
		_objects.overlayObj.after(_objects.contentContainerObj);
		_contentContainerInserted = true;
	};

	function _positionOverlayNode() {
		_setOverlayNodeDimensions();
		_objects.overlayObj.css( { "left": (_config.overlayOffset.left) + "px", "top": (_config.overlayOffset.top) + "px" } );
		_objects.overlayObj.width(_config.overlayWidth);
		_objects.overlayObj.height(_config.overlayHeight);
	};

	function _positionContentContainerNode() {
		if (_objects.contentContainerObj === null) {
			return;
		}
		_setContentContainerNodeDimensions();
		// make sure that the content fits within the overlay
		if (_objects.contentContainerObj.width() > _config.overlayWidth) {
			_objects.contentContainerObj.width(_config.overlayWidth);
			_config.contentContainerOffset.left = _config.overlayOffset.left;
		}
		if (_objects.contentContainerObj.height() > _config.overlayHeight) {
			_objects.contentContainerObj.height(_config.overlayHeight);
			_config.contentContainerOffset.top = _config.overlayOffset.top;
		}
		_objects.contentContainerObj.css( { "left": (_config.contentContainerOffset.left) + "px", "top": (_config.contentContainerOffset.top) + "px" } );
	};

	/**
	 * @description This method is used as a callback
	 */
	function _hideOverlay(fnAfter) {
		fnAfter = (fnAfter) ? fnAfter : function () {};
		if (_objects.overlayObj === null) {
			return;
		}
		_objects.overlayObj.remove();
		fnAfter();
	};

	function _hideOverlayHandler(event) {
		event.preventDefault();
		hideOverlay();
	};

	/**
	 * @description Look for elements within the overlay content that have
	 * 		_config.overlayCloseClassName class, and attach a click handler
	 */
	function _attachContentContainerCloseHandlers() {
		if (_objects.contentContainerObj === null) {
			return;
		}
		_objects.contentContainerObj
			.find("." + _config.overlayCloseClassName)
			.click(_hideOverlayHandler)
		;
	};

	function _showContent(callback) {
		callback = (callback === undefined) ? function () {} : callback;
		if (_objects.contentContainerObj === null) {
			return;
		}
		_positionContentContainerNode();
		_objects.contentContainerObj.children().show(10, callback);
	};

	// PUBLIC METHODS

	function setContent(content) {
		if (content === undefined || content === null || content === '') {
			content = ' ';
		}
		_config.content = content;
		_initContentContainer();
	};

	function getContent() {
		if (_objects.contentContainerObj === null) {
			return '';
		}
		return(_objects.contentContainerObj.html());
	};

	function showOverlay(withContent, showParams, fnAfter) {
		_objects.overlayObj.remove();
		init();
		withContent = (withContent === undefined) ? false : withContent;
		if (typeof(showParams) == "function") {
			fnAfter = showParams;
			showParams = _config.fadeInParams;
		} else {
			fnAfter = function () {};
			showParams = (showParams === undefined) ? _config.fadeInParams : showParams;
		}
		var fnAfterFadeIn = (withContent)
			? function () {
					showContent(undefined, fnAfter);
				}
			: fnAfter
		;
		_objects.overlayObj.fadeTo('fast', _config.opacity, function () {
			_objects.overlayObj.fadeIn(showParams, fnAfterFadeIn);
		});
	};

	function hideOverlay(hideParams, fnAfter) {
		fnAfter = (fnAfter) ? fnAfter : function () {};
		hideParams = (hideParams === undefined) ? _config.fadeOutParams : hideParams;
		hideContent(hideParams, function () {
			_hideOverlay(fnAfter);
		});
	};

	function toggleOverlay(withContent, toggleParams, fnAfter) {
		fnAfter = (fnAfter) ? fnAfter : function () {};
		init();
		withContent = (withContent === undefined) ? false : withContent;
		if (_objects.overlayObj.is(":visible")) {
			hideOverlay(toggleParams, fnAfter);
		} else {
			showOverlay(withContent, toggleParams, fnAfter);
		}
	};

	function showContent(showParams, fnAfter) {
		fnAfter = (fnAfter) ? fnAfter : function () {};
		showParams = (showParams === undefined) ? _config.fadeInParams : showParams;
		_initContentContainer();
		_objects.contentContainerObj.show(showParams, function () {
			_showContent(fnAfter);
		});
	};

	function hideContent(hideParams, fnAfter) {
		hideParams = (hideParams === undefined) ? _config.fadeOutParams : hideParams;
		fnAfter = (fnAfter) ? fnAfter : function () {};
		if (_objects.contentContainerObj === null) {
			return;
		}
		// make sure the inserted content is hidden (IE* doesn't seem to show it):
		_objects.contentContainerObj.children().hide(0);
		_objects.contentContainerObj.fadeOut(hideParams, fnAfter);
	};

	function toggleContent(toggleParams, fnAfter) {
		fnAfter = (fnAfter) ? fnAfter : function () {};
		init();
		if (_objects.contentContainerObj.is(":visible")) {
			hideContent(toggleParams, fnAfter);
		} else {
			showContent(toggleParams, fnAfter);
		}
	};

	function init(params) {
		/*if (_initialised) {
			return;
		}*/
		_initParams(params);
		_initDom();
		_initialised = true;
	};

	// Constructor code
	init(params);

	// Export the public methods
	return {
		init			: init,
		setContent		: setContent,
		getContent		: getContent,
		showOverlay		: showOverlay,
		hideOverlay		: hideOverlay,
		toggleOverlay	: toggleOverlay,
		showContent		: showContent,
		hideContent		: hideContent,
		toggleContent	: toggleContent,
		updateOverlay	: _positionOverlayNode
	};

};

/* EOF */
