if (typeof mcd === 'undefined') {
	throw "mcd.dom and mcd.event are required libraries.";
}

(function () {

	/**
	 * The overlay constructor
	 * @param {String} The id of the element to attach overlay behavior to
	 */
	mcd.Overlay = function (id) {

		/* Map the overlay to a parent element */
		this.parent = document.getElementById(id);

		/* Prepare a configuration object */
		this.configuration = {};

		/* Create an IFRAME shim to allow backgrounds even in Windows
		 * Accessibility mode */
		this.iframe = mcd.dom.getElement(id+'-shim');
		if (!this.iframe) {
			this.iframe = document.createElement('iframe');
			this.iframe.className = 'overlay-shim hide';
			this.iframe.style.zIndex = '1';
			this.iframe.setAttribute('src', 'javascript:false');
			this.iframe.setAttribute('id', id + '-shim');
			this.parent.parentNode.appendChild(this.iframe);
		}
		/* Apply a global click handler on the overlay to search for
		 * terminators */
		mcd.event.add(id, 'click', function (event) {
			mcd.event.preventDefault(event);
			var target = mcd.event.getTarget(event);
			if (this.configuration.terminators) {
				// Bubble up, looking for the target in terminators 
				while (target !== null) {
					if (target.id) {
						if (target.id in this.configuration.terminators) {
							if (this.configuration.terminators[target.id]()) {
								this.hide();
							}
							mcd.event.cancelBubble(event);
							break;
						}
					}

					target = target.parentNode;
				}
			}
			else {
				this.hide();
			}
		}, this);
	};

	/**
	 * The show method removes the hide class for the overlay and turns
	 * the iFrame's display to block.
	 */
	mcd.Overlay.prototype.show = function () {
		mcd.dom.removeClass(this.parent, 'hide');
		mcd.dom.removeClass(this.iframe, 'hide');
	};

	/**
	 * The hide method adds the hide class to the overlay and turns the
	 * iFrame's display to none.
	 */
	mcd.Overlay.prototype.hide = function () {
		mcd.dom.addClass(this.parent, 'hide');
		mcd.dom.addClass(this.iframe, 'hide');
	};

	/**
	 * The toggle method toggles the hide class on the overlay and turns the
	 * iframe's style from block to none and vice-versa
	 */
	mcd.Overlay.prototype.toggle = function () {
		mcd.dom.toggleClass(this.parent, 'hide');
		mcd.dom.toggleClass(this.iframe, 'hide');
	};

	/**
	 * The configure method passes the parameters object to the configuration
	 * property
	 * @param {Object}
	 */
	mcd.Overlay.prototype.configure = function (parameters) {
		this.configuration = parameters || {};
	};

	/**
	 * A method that sets the position of the overlays and iFrames
	 * @param	{Object|HTMLElement} The parameters relating to the
	 * 			positioning of the iFrame that is either an object
	 * 			literal or an HTML Element
	 */
	mcd.Overlay.prototype.setPosition = function (element) {
		var parentID = this.parent.id;
		var xyOffsets = [];

		/* Try to get the position from the configuration. If it can't be found
		 * grab the position from the element */
		if (this.configuration.position) {
			xyOffsets[0] = this.configuration.position.x || 0;
			xyOffsets[1] = this.configuration.position.y || 0;
		}
		else {
			xyOffsets = mcd.dom.getPosition(element);
		}

		/* Try to offset the location of the overlay */
		if (this.configuration.offset) {
			xyOffsets[0] += this.configuration.offset.x || 0;
			xyOffsets[1] += this.configuration.offset.y || 0;
		}

		// Create a new array of x and y coordinates for the iframe
		// that take into account both the coordinates of the overlay
		// and a 3px buffer.
		var iframeXY = [
			xyOffsets[0] + 3,
			xyOffsets[1] + 3
		];

		mcd.dom.setPosition(this.parent, xyOffsets, true);
		mcd.dom.setPosition(this.iframe, iframeXY, true);

	};

	mcd.Overlay.prototype.resetOverlay = function () {
		/* Sets Width & Height b/c offsetWidth & offsetHeight are not set until box is rendered */
		this.iframe.style.width = (this.parent.offsetWidth - 7) + 'px';
		this.iframe.style.height = (this.parent.offsetHeight - 7) + 'px';
		
	}

	mcd.Overlay.prototype.registerTerminator = function ( id, callback ) {
		this.terminators[id] = callback;
	};

	mcd.Overlay.prototype.setTrigger = function (trigger) {
		mcd.event.add(trigger, 'click', function (event) {
			mcd.event.preventDefault(event);
			
			this.toggle();

			/* Sets Width & Height b/c offsetWidth & offsetHeight are not set until box is rendered */
			this.iframe.style.width = (((this.parent.offsetWidth - 7) < 0) ? 0 : (this.parent.offsetWidth - 7)) + 'px';
			this.iframe.style.height= (((this.parent.offsetHeight - 7) < 0) ? 0 : (this.parent.offsetHeight - 7)) + 'px';

			/* If the object is visible, set its position */
			if (!mcd.dom.hasClass(this.parent, 'hide')) {
				this.setPosition(trigger);
			}
		}, this);
	};
	/**
	 * The MCD Overlay Manager Library
	 * @namespace mcd.Overlay
	 * @class     mcd.Overlay.manager
	 */
	mcd.Overlay.manager = function () {

		// The private API
		var _this = {

			/**
			 * An overlay callback for click events
			 * @param   {Event} The click event
			 * @private
			 */
			triggerClickCallback : function (event) {
				mcd.event.preventDefault(event);

				var targetedElement = mcd.event.getTarget(event);

				/* Bubble up looking for the anchor w/the rel */
				while (!targetedElement.getAttribute('rel') && targetedElement !== null) {
					targetedElement = targetedElement.parentNode;
					break;
				}

				var overlayInstance = mcd.Overlay.manager.getOverlay(targetedElement.getAttribute('rel'));

				/* Toggle the visibility of the overlay */
				overlayInstance.toggle();

				/* Sets Width & Height b/c offsetWidth & offsetHeight are not set until box is rendered */
				overlayInstance.iframe.style.width = (((overlayInstance.parent.offsetWidth - 7) < 0) ? 0 : (overlayInstance.parent.offsetWidth - 7)) + 'px';
				overlayInstance.iframe.style.height= (((overlayInstance.parent.offsetHeight - 7) < 0) ? 0 : (overlayInstance.parent.offsetHeight - 7)) + 'px';



				/* If the object is visible, set its position */
				if (!mcd.dom.hasClass(overlayInstance.parent, 'hide')) {
					overlayInstance.setPosition(targetedElement);
				}
			}
		};

		// The public API
		return {
			overlays : {},

			/**
			 * The overlay manager initilaization routine
			 *
			 * @param  {Object} An object of parameters
			 * @return mcd.Overlay.manager
			 */
			init : function (parameters) {
				var parameters = parameters || {};
				var triggerList = mcd.dom.getElementsByAttribute('class', 'overlay-trigger', document.body, 'a', true);

				for (var i = 0; i < triggerList.length; i++) {
					var id = triggerList[i].getAttribute('rel');

					if (id) {
						/* Instantiate the new overlay */
						this.overlays[id] = new mcd.Overlay(id);

						/* Configure the overlay */
						if (parameters[id]) {
							this.overlays[id].configure(parameters[id]);
						}

						/* Apply a click event to the overlay */
						mcd.event.add(triggerList[i], 'click', _this.triggerClickCallback);
					}
				}

				return this;
			},

			getOverlay : function (id) {
				if (id in this.overlays) {
					return this.overlays[id];
				}
			},

			hideAll : function () {
				for (var i in this.overlays) {
					this.overlays[i].hide();
				}

				return this;
			},

			showAll : function () {
				for (var i in this.overlays) {
					this.overlays[i].show();
				}

				return this;
			},

			toggleAll : function () {
				for (var i in this.overlays) {
					this.overlays[i].toggle();
				}

				return this;
			}
		};
	}();
})();