/*jslint unparam: true, browser: true, sloppy: true, white: true, nomen: true, maxerr: 50, indent: 4 */

if (typeof(ID) === 'undefined') { throw "The ID class is required for OverlayHandler.js"; }

//-------------------------------------------------------------------------------------------------------------
// To show an element:
//
//     document.fire('overlaywindow:show', {'element': foobar});
//
// To hide an element:
//
//     Click an element with class "action_overlaywindowclose"
//     document.fire('overlaywindow:show');
// 

ID.OverlayHandler = Class.create(ID,{
	toString: function() { return 'ID.OverlayHandler'; },
	initialize: function(options) {
		this.options = Object.extend({
			// Master element
			'elementId':					'overlaywindowhandler',
			'elementClass':					'overlaywindowhandler',
			// Container for window elements
			'containerElementClass':		'overlaywindowcontainer',
			// Backdrop element
			'backdropElementClass':			'overlaywindowbackdrop',
			// Window stuff
			'windowElementSelector':		'.overlaywindow',
			'windowElementHandledClass':	'overlaywindowhandled',
			'windowAutoOpenClass':			'overlaywindowautoopen',
			// Effects
			'backdropOpacity':				0.7,
			'backdropAppearDuration':		0.5,
			'backdropFadeDuration':			0.2,
			'windowAppearDuration':			0.5,
			'windowFadeDuration':			0.2
		},options || {});

		this.visibleElement = undefined;
		this.backdropVisible = false;

		// Get master element
		this.element = $(this.options.elementId);
		if (this.element === null) {
			this.element = new Element('div',{'id':this.options.elementId})
				.addClassName(this.options.elementClass)
				.hide();
			$$('body').first().insert({'top':this.element});
		}

		// Get container element
		this.containerElement = this.element.down('.'+this.options.containerElementClass);
		if (this.containerElement === undefined) {
			this.containerElement = new Element('div')
				.addClassName(this.options.containerElementClass);
			this.element.insert({'top':this.containerElement});
		}

		// Get backdrop element
		this.backdropElement = this.element.down('.'+this.options.backdropElementClass);
		if (this.backdropElement === undefined) {
			this.backdropElement = new Element('div')
				.addClassName(this.options.backdropElementClass)
				.hide();
			this.element.insert({'top':this.backdropElement});
		}

		this._onDomUpdated.bind(this).defer();
	},
	_onDomUpdated: function() {
		this._resize();
		this._scan();
		this._autoopen();

		// Redirect action events (from ActionClickHandler)
		document.observe('action:showoverlaywindow', (function(evt){
			this._debug('Forwarding action event to overlaywindow event');
			document.fire('overlaywindow:show',{'element':evt.memo.data.overlaywindow});
		}).bindAsEventListener(this));

		document.observe('overlaywindow:show', this._onShowEventReceived.bindAsEventListener(this));
		document.observe('overlaywindow:refresh', this._onRefreshEventReceived.bindAsEventListener(this));
		document.observe('custom:contentrefresh', this._onRefreshEventReceived.bindAsEventListener(this));
		Event.observe(window,'resize', this._onWindowResize.bindAsEventListener(this));
		this.containerElement.observe('click', this._onContainerClick.bindAsEventListener(this));
	},
	_onWindowResize: function(e) {
		this._resize();
	},
	_onContainerClick: function(e) {
		var element = Event.element(e);

		// Did we click the backdrop?
		if (element.hasClassName(this.options.containerElementClass)) {
			this._hide();
			return;
		}

		// Did we click a close element?
		if (element.hasClassName('action_overlaywindowclose') || element.up('.action_overlaywindowclose')) {
			Event.stop(e);
			this._hide();
			return;
		}

		// Still here?
		this._debug('Received click event in container: '+element.inspect());
	},
	_onRefreshEventReceived: function(e) {
		this._debug('Refresh event received');
		this._scan();
	},
	_onShowEventReceived: function(e) {
		this._debug('Show event received');

		// Just in case
		this._scan();

		if (e.memo.element === undefined) {
			this._hide();
		} else {
			var element = $(e.memo.element);
			if (element === null) {
				this._error('Element not found');
			} else {
				this._show(element);
			}
		}
	},
	_autoopen: function() {
		var element = $$('.'+this.options.windowAutoOpenClass).first();
		if (element !== undefined) {
			this._info('Auto-opening element..');
			this._show(element);
		}
	},
	_fire: function(eventName, memo) {
		var fullEventName = 'overlaywindow:'+eventName;
		this._debug('Firing event: '+fullEventName);
		document.fire(fullEventName, memo);
	},
	_resize: function() {
		// Make sure the overlay container has the same width as the page
		var viewportDim = document.viewport.getDimensions();
		var pageWidth = viewportDim.width;
		var pageHeight = viewportDim.height;

		this.containerElement.setStyle({
			'height':	''+pageHeight+'px'
		});
		this.backdropElement.setStyle({
			'height':	''+pageHeight+'px'
		});
	},
	_hide: function() {
		this._hideVisibleElement(this._hideBackdrop.bind(this));
	},
	_hideVisibleElement: function(callback) {
		if (this.visibleElement !== undefined) {
			this._info('Fading visible element..');
			this.visibleElement.fade({
				'duration':		this.options.windowFadeDuration,
				'afterFinish':	(callback === undefined ? undefined : callback)
			});
			this.visibleElement = undefined;
		} else {
			try {
				callback();
			} catch(ex) {
				this._error('Callback failed: '+ex);
			}
		}
	},
	_hideBackdrop: function() {
		if (this.backdropVisible) {
			this._info('Fading backdrop..');
			this.backdropElement.fade({
				'duration':		this.options.backdropFadeDuration,
				'afterFinish':	this._hideMasterElement.bind(this)
			});
			this.backdropVisible = false;
		}
	},
	_hideMasterElement: function() {
		this.element.hide();
		this._fire('hidden');
	},

	_show: function(element) {
		this._info('Showing: '+element.identify());

		/* Check if this element is outside the container */
		if (!element.hasClassName(this.options.windowElementHandledClass)) {
			this._moveElementToContainer(element);
		}

		this._showBackdrop(this._hideVisibleElement.bind(this,this._showElement.bind(this,element)));
	},
	_showBackdrop: function(callback) {
		this.element.show();
		if (!this.backdropVisible) {
			this._info('Appearing backdrop..');
			this.backdropElement.appear({
				'to':			this.options.backdropOpacity,
				'duration':		this.options.backdropAppearDuration,
				'afterFinish':	(callback === undefined ? undefined : callback)
			});
			this.backdropVisible = true;
		} else {
			if (callback) {
				try {
					callback();
				} catch(ex) {
					this._error('Callback failed: '+ex);
				}
			}
		}
	},
	_showElement: function(element) {
		this._info('Appearing element #'+element.identify());
		this.visibleElement = element;
		this.visibleElement.appear({
			'duration':		this.options.windowAppearDuration,
			'afterFinish':	this._onElementAppeared.bind(this,element)
		});
	},
	_onElementAppeared: function(element) {
		this._fire('visible_'+element.identify());
		this._fire('visible',{'element':element});
	},
	_moveElementToContainer: function(element) {
		this._info('Moving element to container: #'+element.identify());
		element.hide();
		element.addClassName(this.options.windowElementHandledClass);
		this.containerElement.insert({'bottom':element});
	},
	_scan: function() {
		var elements = $$(this.options.windowElementSelector);
		elements.each(function(element){
			if (!element.hasClassName(this.options.windowElementHandledClass)) {
				this._moveElementToContainer(element);
			}
		},this);
	}
});


