var BlackBox = Class.create({
	// Constants
	version:	1.3,
	
	// State Flags
	frozen:	false,
	locked:	false,
	
	// Structural Elements
	mask:				null,
	wrapper:		null,
	container:	null,
	
	// Settable Properties
	mask_opacity:	0.75,
	
	container_apply_borders:	false,
	
	fx_use:				true,
	fx_duration:	0.25,
	
	// Initialize
	initialize: function(params)
	{
		// Ensure settable parameter object
		if(!params) params = {};
		
		// Settable mask parameters
		if(typeof(params.mask) != 'undefined')
		{
			if(typeof(params.mask.opacity) != 'undefined') this.mask_opacity = params.mask.opacity;
		}
		
		// Settable container parameters
		if(typeof(params.container) != 'undefined')
		{
			if(typeof(params.container.borders) != 'undefined') this.container_apply_borders = params.container.borders;
		}
		
		// Settable fx parameters
		if(typeof(params.fx) != 'undefined')
		{
			if(typeof(params.fx.use) != 'undefined') this.fx_use = params.fx.use;
			if(typeof(params.fx.duration) != 'undefined') this.fx_duration = params.fx.duration;
		}
	},
	
	// Create/position mask
	createMask: function()
	{
		// Create element
		var mask = new Element('div', {id:'blackbox-mask'});
		
		// Set required styles
		mask.setStyle(
		{
			display:					'none',
			position:					'fixed',
			top:0, left:0,
			zIndex:						1000,
			opacity:					this.mask_opacity
		});
		
		// Set behaviours
		Event.observe(mask, 'click', this.hide.bind(this));
		Event.observe(window, 'resize', this.positionMask.bind(this));
		
		// IE6 specific overrides as it cannot understand position:fixed
		if(/MSIE 6/i.test(navigator.userAgent)) 
		{
			// Set position abolute
			mask.setStyle({position:'absolute'});
			
			// Add reposition on scroll
			Event.observe(window, 'scroll', this.positionMask.bind(this));
		}
		
		// Add to instance
		this.mask = mask;
	},
	positionMask: function(event)
	{
		// Set height and width of mask to match the viewport
		this.mask.setStyle({width:document.viewport.getDimensions().width+'px', height:document.viewport.getDimensions().height+'px'});
		
		// For IE6, reposition the mask to sit at the top of the viewport (using scroll offset)
		if(/MSIE 6/i.test(navigator.userAgent)) this.mask.setStyle({top:document.viewport.getScrollOffsets().top + 'px'});
	},
	
	// Create/position content container
	createContainer: function()
	{
		// Create the wrapper
		var wrapper = new Element('div', {id:'blackbox-wrapper'});
		
		// Set required styles
		wrapper.setStyle({
			display:	'none',
			position:	'fixed',
			zIndex:		this.mask.getStyle('zIndex') + 1
		});
		
		// Add corner/border elements
		if(this.container_apply_borders)
		{
			wrapper.appendChild(new Element('div').addClassName('box-cnr top left'));
			wrapper.appendChild(new Element('div').addClassName('box-bdr top'));
			wrapper.appendChild(new Element('div').addClassName('box-cnr top right'));
			wrapper.appendChild(new Element('div').addClassName('box-bdr left'));
		}
		
		// Add body
		var container = wrapper.appendChild(new Element('div').addClassName('box-body'));

		// Add remaining corner/borders
		if(this.container_apply_borders)
		{
			wrapper.appendChild(new Element('div').addClassName('box-bdr right'));
			wrapper.appendChild(new Element('div').addClassName('box-cnr bottom left'));
			wrapper.appendChild(new Element('div').addClassName('box-bdr bottom'));
			wrapper.appendChild(new Element('div').addClassName('box-cnr bottom right'));
		}
		
		// Required child element styles
		if(this.container_apply_borders) wrapper.select('div').each(function(element) {element.setStyle({float:'left'});});
		
		// Add behaviours
		Event.observe(window, 'resize', this.positionContainer.bind(this));
		
		// Set IE6 specific overrides as it cannot understand position:fixed
		if(/MSIE 6/i.test(navigator.userAgent)) 
		{
			// Set position to absolute
			wrapper.setStyle({position:	'absolute'});
			
			// Attach reposition method to scroll event
			Event.observe(window, 'scroll', this.positionContainer.bind(this));
		}
		
		// Add wrapper and container to instance
		this.wrapper = wrapper;
		this.container = container;
	},
	positionContainer: function(event)
	{
		// Cancel if container is empty
		if(!this.container.firstDescendant()) return;
		
    /*
			In order to get the dimensions of the content container it has to be on the stage AND 'visible',
			visible however does not mean it has to be within the viewport...
		*/
		
		// Init the content and target dimensions for the wrapper
		var content_dimensions = {width:null, height:null};
		var target_dimensions = {width:null, height:null};
		
		
		// Set temporary styles that place the wrapper out of the viewport
		if(!this.wrapper.visible())
		{
			this.wrapper.setStyle({width:'auto', height:'auto', top:'-9999px', left:'-9999px'});
			
			// Set wrapper to visble
			this.wrapper.show();
			
			content_dimensions = this.container.firstDescendant().getDimensions();
			target_dimensions = this.container.firstDescendant().getDimensions();
			
			// Hide the wrapper
			this.wrapper.hide();
		}
		else 
		{
			content_dimensions = this.container.firstDescendant().getDimensions();
			target_dimensions = this.container.firstDescendant().getDimensions();
		}
		
		// Where the container has borders
		if(this.container_apply_borders)
		{
			// Resize borders to fit content
			this.wrapper.down('.box-bdr.top').setStyle({width:(content_dimensions.width)+'px'});
			this.wrapper.down('.box-bdr.right').setStyle({height:(content_dimensions.height)+'px'});
			this.wrapper.down('.box-bdr.bottom').setStyle({width:(content_dimensions.width)+'px'});
			this.wrapper.down('.box-bdr.left').setStyle({height:(content_dimensions.height)+'px'});
			
			// Get the cumulative container/border sizes
			var border_offsets = {
				width: parseInt(this.wrapper.down('.box-bdr.left').getStyle('width')) + parseInt(this.wrapper.down('.box-bdr.right').getStyle('width')),
				height: parseInt(this.wrapper.down('.box-bdr.top').getStyle('height')) + parseInt(this.wrapper.down('.box-bdr.bottom').getStyle('height'))
			};
			
			// Add the border dimensions to the wrapper target dimensions
			target_dimensions.width		= content_dimensions.width + border_offsets.width;
			target_dimensions.height	= content_dimensions.height + border_offsets.height;			
		}
		
		// Resize and reposition content to fit content/viewport
		this.wrapper.setStyle({
			width:	(target_dimensions.width) + 'px',
			height:	(target_dimensions.height) + 'px',
			top:		(document.viewport.getDimensions().height/2 - (target_dimensions.height)/2) + 'px',
			left:		(document.viewport.getDimensions().width/2 - (target_dimensions.width)/2) + 'px'
		});
		
		// For IE6, reposition the mask to sit at the top of the viewport (using scroll offset)
		if(/MSIE 6/i.test(navigator.userAgent)) this.wrapper.setStyle({top:	(document.viewport.getScrollOffsets().top + (document.viewport.getDimensions().height/2 - (target_dimensions.height)/2)) + 'px'});
	},
	
	// Set/clear modal content
	write: function(content, params)
	{
		// Create mask and wrapper/container if required
		if(!this.mask) this.createMask();
		if(!this.container) this.createContainer();
		
		// Ensure optional parameters
		if(typeof(params) == 'undefined') params = {};
		
		// Where class has been supplied, add to wrapper
		if(typeof(params.override_class) != 'undefined') this.wrapper.addClassName(params.override_class);
		
		// Lock mask if required (eg. for true modal window behaviours)
		if(typeof(params.lock) != 'undefined') this.locked = params.lock;
		
		// Append content to container
		this.container.update(content);
		
		// Validate supplied content
		if(!this.container.firstDescendant())
		{
			// Use broadcast to display validation results
			return;
		}
		
		// Display
		this.display();
	},
	clear: function()
	{
		// Erase all content from modal box body
		this.container.removeChild(this.container.firstDescendant());
		
		// Clear any set classes from wrapper
		this.wrapper.setAttribute('class','');
	},
	
	// Display/hide the modal window
	display: function()
	{
		// Cancel if frozen (locked for animation)
		if(this.frozen) return;
		
		// Add mask to the stage
		document.body.appendChild(this.mask);

		// Position mask
		this.positionMask();
		
		// Display mask
		if(this.fx_use && !this.mask.visible()) this.mask.appear(
		{
			to:						this.mask_opacity, 
			duration:			this.fx_duration, 
			queue:				{scope:'BlackBox:MASK', position:'end'},
			beforeStart:	function() { this.frozen = true; }.bind(this),
			afterFinish:	function() { this.frozen = false; }.bind(this)
		});
		else this.mask.show();
		
		// Add wrapper to stage
		document.body.appendChild(this.wrapper);
		
		// Position container via wrapper
		this.positionContainer();
		
		// Display wrapper/container
		if(this.fx_use) this.wrapper.appear(
		{
			duration:	this.fx_duration, 
			queue:		{scope:'BlackBox:WRAPPER', position:'end'}
		});
		else this.wrapper.show();
	},
	hide: function(event, unlock)
	{
		// Stop event propogation
		if(event) Event.stop(event);
		
		if(event && (this === event.target || this === window) && typeof(window.blackbox) != 'undefined')
		{
			var hide = blackbox.hide.bind(blackbox, event, (unlock)? true: false);
			return hide();
		}
		
		// Unlock of argument supplied
		if(unlock) this.locked = false;
		
		// Cancel if locked
		if(this.locked || this.frozen) return;
		
		// Hide and remove mask from stage
		if(this.fx_use) this.mask.fade(
		{
			from:					this.mask_opacity,
			duration:			this.fx_duration,
			queue:				{scope:'BlackBox:MASK', position:'end'},
			beforeStart:	function() { this.locked = true; }.bind(this),
			afterFinish:	function() { this.locked = false; this.mask.remove(); }.bind(this)
		});
		else 
		{
			this.mask.hide();
			this.mask.remove();
		}
		
		// Hide and remove wrapper from stage
		if(this.fx_use) this.wrapper.fade(
		{
			duration:			this.fx_duration,
			queue:				{scope:'BlackBox:WRAPPER', position:'end'},
			afterFinish:	function() { this.wrapper.remove(); this.clear(); }.bind(this)
		});
		else 
		{
			this.wrapper.hide();
			this.wrapper.remove();
			this.clear();
		}
	}
});

var blackbox = new BlackBox();
