(function($j){

	Code.registerNamespace('Code.Extensions');
	
	/*
	 * Class: Code.Extensions.Modal
	 * Requires jQuery and BgIFrame
	 */
	Code.Extensions.Modal = {
		
		_eventNamespaces: {
			windowResize: 'resize.code.extensions.modal.window',
			keyUp: 'keyup.code.extensions.modal.document',
			closeButtonClick: 'click.code.extensions.closebutton'
		},
		_maskjObj: null,
		_maskClickerjObj: null,
		_modaljObj: null,
		_newModalCreated: null,
		_settings: null,
		
		
		_onWindowResized: function(e){
			
			var self = this;
			
			// Reset the mask dimensions
			self._setMaskDimensions();
			
			// Reset the position of the modal
			this.refreshModal();

		},
		
		
		_setMaskDimensions: function(){
		
			
			if (Code.UserAgent.isIElt7){
				var self = this;
				self._maskjObj.css({
					width: $j(window).width() + 'px',
					height: $j(document).height() + 'px'
				});
			}
			
		},
		
		
		/*
		 * Function: show
		 * 
		 * Parameters:
		 *	object: - settings - overide default settings (onShow, maskBgStyle, maskOpacity, maskAnimationSpeed, modalContentHtml, modalContentSelector, modalContentEl, zIndex) 
		 */
		show: function(options){
			
			var self = this;
			
			self._settings = Code.mergeObjects(
				{
					blockUI: false,
					onBeforeShow: null,
					onShow: null,
					onClose: null,
					maskBgStyle: '#000000',
					maskOpacity: 0.6,
					maskAnimationSpeed: 'fast',
					modalContentHtml: '<div style="background: white; padding: 50px; width: 100px; height: 50px;">Modal Window</div>',
					modalContentSelector: null,
					modalContentEl: null,
					modalShowFunction: null,
					zIndex: 100000
				}, 
				options);
							
			// Clean up an existing modal
			Code.Extensions.Modal.close();
						
			self._newModalCreated = false;
			
			// Create the mask
			self._maskjObj = $j('<div style="top: 0; left: 0; z-index:' + self._settings.zIndex + '; background:' + self._settings.maskBgStyle + '; display: none;"></div>');
			
			if (Code.UserAgent.isIElt7){
				self._maskjObj.css({
					position: 'absolute'
				});
				self._setMaskDimensions();
			}
			else{
				self._maskjObj.css({
					position: 'fixed',
					width: '100%',
					height: '100%'
				});
			}
			
			
			// Create the mask clicker. This is an invisible full width and height later inside the mask purely for handling the click
			// event for the mask. This is really only required for IE6
			self._maskClickerjObj = $j('<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></div>');
			
			self._maskClickerjObj.appendTo(self._maskjObj);
			
			if (!self._settings.blockUI){
				// Add click event to mask clicker
				self._maskClickerjObj.click(function(){
					Code.Extensions.Modal.close();
				});
				
				// Add event handler to the escape key
				$j(document).bind(self._eventNamespaces.keyUp, function(e){
					if (e.keyCode == 27) {
						Code.Extensions.Modal.close();
					}
				});
			}
			
			if (Code.Type.isFunction(self._settings.onBeforeShow)){
				self._settings.onBeforeShow();
			}
			
			// Append the mask to the body
			$j('body').append(self._maskjObj);
			
			// Show the mask
			self._maskjObj
				.bgiframe()
				.show()
				.css({opacity: 0})
				.fadeTo(self._settings.maskAnimationSpeed, self._settings.maskOpacity, function(){
					
					self.replaceModal(self._settings);

					// Bind window resize events
					$j(window).bind(self._eventNamespaces.windowResize, function(e){
						self._onWindowResized(e);
					});
			
			});
		},
		
		
		/*
		 * Function: replaceModal
		 * Replaces the current modal with another
		 */
		replaceModal: function(settings){
			
			var self = this;
			
			self.removeModal();
			
			if (!Code.Type.isNothing(settings.modalContentEl)){
				self._modaljObj = $j(settings.modalContentEl);
			}
			else if (!Code.Type.isNothing(settings.modalContentSelector)){
				var modals = $j(settings.modalContentSelector);
				if (modals.length > 0){
					self._modaljObj = $j(modals[0]);
				}
				else{
					throw 'Unable to find modalContentSelector "' + settings.modalContentSelector + '"';
				}
			}
			else {
				// Create a new jQuery object
				self._modaljObj = $j(settings.modalContentHtml);
				self._newModalCreated = true;
			}
			
			if (Code.Type.isNothing(self._modaljObj)){
				throw 'Unable to find any modal content';
			}
			
			self._modaljObj.remove();
			$j('body').append(self._modaljObj);
			
			self.refreshModal();
			
		},
		
		
		/*
		 * Function: removeModal
		 * Removes current modal window. Will not remove mask
		 */
		removeModal: function(){
			
			var self = this;
			
			if (!Code.Type.isNothing(self._modaljObj)){
				
				$j('.js-close-modal', self._modaljObj)
					.die(self._eventNamespaces.closeButtonClick);
			
				if (self._newModalCreated){
					self._modaljObj.remove();
					self._modaljObj = null;
				}
				else{
					self._modaljObj.hide();
				}
			}
			
		},
		
		
		/*
		 * Function: close
		 */
		close: function(){
			
			var self = this;
			
			// Unbind events
			$j(window).unbind(self._eventNamespaces.windowResize);
			$j(document).unbind(self._eventNamespaces.keyUp);
			
			self.removeModal();
			
			if (!Code.Type.isNothing(self._maskjObj)){
				self._maskjObj.remove();
				self._maskjObj = null;
			}
			
			if (Code.Type.isFunction(self._settings.onClose)){
				self._settings.onClose();
			}
			
				
		},
		
		
		/*
		 * Function: refreshModal
		 * Refreshes the modal windows position.
		 */
		refreshModal: function(){
		
			var self = this;
					
			// Center the modal
			var scrollTop = $j(window).scrollTop();
			var winH = window.innerHeight ? window.innerHeight : $j(window).height();;
			var winW = $j(window).width();
			var top = (winH/2-self._modaljObj.outerHeight()/2) + scrollTop;
			var left = (winW/2-self._modaljObj.outerWidth()/2);
			
			// Quick fix to prevent negative positionning
			if (top < 0){ top = 0; }
			if (left < 0){ left = 0; }
			
			// Show the modal
			self._modaljObj
				.css({
					position: 'absolute',
					top: top,
					left: left,
					zIndex: self._settings.zIndex + 1
				});
			
			$j('.js-close-modal', self._modaljObj)
				.die(self._eventNamespaces.closeButtonClick)
				.live(self._eventNamespaces.closeButtonClick, 
					function(e){ Code.Extensions.Modal.close(); e.preventDefault(); }
				);
				
			if (Code.Type.isFunction(self._settings.modalShowFunction)){
				self._settings.modalShowFunction(self._modaljObj, self._settings.onShow);
			}
			else {
				self._modaljObj.show();
				if (Code.Type.isFunction(self._settings.onShow)){
					self._settings.onShow();
				}
			}
			
		},
		
		
		/*
		 * Function: getModal
		 */
		getModal: function(){
			var self = this;
			return self._modaljObj;
		},
		
		
		/*
		 * Function: getMask
		 */
		getMask: function(){
			var self = this;
			return self._maskjObj;
		}
		
	}
	
})(jQuery);
