/**
 * NS Dialogs manager
 *
 * @version   1.00.090522
 * @author    LBI Lost Boys
 */
NS(function($){

	var	DIALOG_ORIENTATION = 'right center';
	var DIALOG_OFFSET = 20;
		
	var PREFIX = "dialog-";
	
	var ACTION_CONFIRM = "confirm";
	var ACTION_CANCEL = "cancel";
	var ACTION_SUBMIT = "submit";
	var ACTION_CLOSE = "close";

	var CSS_INVISIBLE = { visibility: 'hidden', display: 'block' };
	var CSS_NODISPLAY = { display: 'none', visibility: 'visible' };
		
	var REG_ACTION_TYPE = new RegExp(PREFIX + "([^ ]+)");
	var REG_RELATION = new RegExp("(^|\\s)" + PREFIX);

	var REG_ALIGNCENTER = new RegExp('center');
	var REG_ALIGNMENTS = new RegExp('(left|right|top|bottom)','gi');
	var ALIGN_LEFT = 'left'; 
	var ALIGN_RIGHT = 'right'; 
	var ALIGN_TOP = 'top'; 
	var ALIGN_BOTTOM = 'bottom'; 
	
	/**
	 * Global dialog manager
	 */
	NS.Dialogs = {
		types:   {},
		dialogs: {},

		initialize:function() {
			NS.relateLink(REG_RELATION, this.handleClick.bind(this) /*, null */);
			
			// iframe for covering up select boxes in IE
			this.ieFrame = NS.Interface.createIEFrame();
		},
	
		handleClick:function(link, rel) {
			var type = REG_ACTION_TYPE.exec(rel)[1];
			var current = this.currentDialog;

			switch (type) {
				case ACTION_CLOSE:
					if(current) { current.close(); }
				break;
				case ACTION_CONFIRM:
					if(current) { current.confirm(true); }
				break;
				case ACTION_SUBMIT:
					if(current) { current.submit(); }
				break;
				case ACTION_CANCEL:
					if(current) { current.confirm(false); }
				break;
				default:
					this.open(type, link);
				break;
			}

			return true;
		},

		open:function(type, origin) {
			var dialog = this.getDialog(type);
			this.setCurrent(dialog);
			
			dialog.open(origin);
			
		/*	if(!this.closeHandler) {
				this.closeHandler = this.close.bind(this);
				NS.subscribe('click', this.closeHandler);
			}
		*/	
			return dialog;
		},

		close:function(e) {
			var current = this.currentDialog;
			if(current && current.closeByEvent(e)) {
				current.close();
				this.setCurrent(null);
			/*	if(this.closeHandler) {
					NS.unsubscribe('click', this.closeHandler);
					this.closeHandler = null;
				}
			*/
			}
		},

		getDialog:function(type) {
			if(!this.dialogs[type]) {
				var Constructor = this.types[type] || NS.Dialog, 
					element = $('#' + PREFIX + type);

				this.dialogs[type] = new Constructor(element, type);
			}

			return this.dialogs[type];
		},

		register:function(type, Dialog) {
			this.types[type] = Dialog;
			return Dialog;
		},

		setCurrent:function(dialog) {
			if(this.currentDialog) {
				this.currentDialog.close();
			}
			this.currentDialog = dialog;
		},
		
		toggleIEFrame:function(toggle, dialog) {
			if(this.ieFrame){
				if(toggle) {
					var offset = dialog.offset();
					this.ieFrame.css({
						left: offset.left + 'px',
						top: offset.top + 'px',
						width: (dialog[0].offsetWidth + 10) + 'px',
						height: (dialog[0].offsetHeight + 8) + 'px'
					});
				} else {
					this.ieFrame.css({
						left: -9999 + 'px',
						top: -9999 + 'px'
					});
				}
			}
		}
	};

	/**
	 * Base dialog class. Extended dialogs inherit from this one.
	 */
	NS.Dialog = function(element, type) {
		this.container = element;
		this.containerID = element.attr('id');
		this.content = $('p', element);
		this.arrow = $('.arrow', element);
		this.type = type;
		this.orientation = DIALOG_ORIENTATION;
		this.offset = DIALOG_OFFSET;
	};

	NS.Dialog.prototype = {
		activate: function(activate) { },
		confirm: function(confirm) { 
			if(confirm) {
				window.location = this.origin.href;
			}
		},

		submit:function() {
			var form = this.container.find('form')[0];
			if(form) {
				NS.forms.submit(form);
			}
		},
		
		show:function() { 
			var dialog = this;
			this.container.stop();
			this.container.fadeIn('fast', function() {
				NS.Dialogs.toggleIEFrame(true, dialog.container);
			}); 
		},
		
		hide:function() { 
			this.container.fadeOut('fast');
			NS.Dialogs.toggleIEFrame(false);
		},
		
		open:function(origin) {
			if(this.containerID) {
				// renew container reference; wicket may have replaced the div for a new one.
				this.container = $(document.getElementById(this.containerID));
			}

			this.origin = origin;
			this.activate(true);
			this.container.css(CSS_INVISIBLE);
			this.container.css(this.getPosition(origin));
			this.container.css(CSS_NODISPLAY);
			this.show();
		},

		close:function() {
			this.activate(false);
			this.hide();
		},

		getPosition:function(origin) {
			
			var doc = document.documentElement, 
				off = this.offset, 
				orgOff = $(origin).offset(),
				pos = this.container.offsetParent().offset(),
				ol = orgOff.left, 
				ot = orgOff.top,
				dw = this.container[0].offsetWidth,
				dh = this.container[0].offsetHeight;

			var availW = window.innerWidth || doc.clientWidth;
			var availH = window.innerHeight || doc.clientHeight;
			var scrollT = window.pageYOffset || doc.scrollTop;
			
			this.container.removeClass('west');
			
			var minTop = scrollT;
			var maxTop = availH + scrollT - dh;

			var x = ol, y = ot;
			if(REG_ALIGNCENTER.test(this.orientation)) {
				x -= (dw - origin.offsetWidth)/2;
				y -= (dh - origin.offsetHeight)/2;
			}

			this.orientation.replace(REG_ALIGNMENTS, function(str, alignment){
				switch(alignment) {
					case ALIGN_LEFT:   x = ol - off - dw; y = limit(y, minTop, maxTop); break;
					case ALIGN_RIGHT:  x = ol + origin.offsetWidth + off; y = limit(y, minTop, maxTop); break;
					case ALIGN_TOP:    y = ot; break;
					case ALIGN_BOTTOM: y = ot + origin.offsetHeight; break;
				}
			});

			if((x + dw) > availW || this.container.hasClass('left')) {
				this.container.addClass('west');
				x = ol - off - dw;
			}

			if (this.container.hasClass('bottom')) {
				var arrowX = (ol - x) - 10;
				this.arrow[0].style.left = arrowX + 'px';
				y += 10;
			}
			var arrowY = (ot - y) + 13;
			this.arrow[0].style.top = arrowY + 'px';
			
			x = x - pos.left;
			y = y - pos.top;
			
			return { left: x + 'px', top: y + 'px' };
		},

		write:function(html) {
			NS.DOM.write(this.content, html);
			this.reflow();
		},

		// redraws the dialog, for instance when new content is loaded via ajax.
		reflow:function() {
			this.container.css(
				this.getPosition(this.origin)
			);
		},

		// close the dialog on a click outside the dialog, which is not on the origin, nor the autocomplete
		closeByEvent:function(e) {
			var contain = this.container[0]; 
			var node = e.target; 
			var autocomplete = document.getElementById("autoComplete");

			while(node) {
				if(node == contain || node == this.origin || node == autocomplete) {
					return false;
				}
				node = node.parentNode;
			}
			return true;
		}
	};

	var limit = function(value, min, max) {
		return Math.min(Math.max(value, min), max);
	};

	
	/**
	 * EXTENDED dialogs
	 *
	 * The dialogs below are global dialogs, used throughout the site by multiple applications. For
	 * application specific dialogs, see / use application script files.
	 */

	
	/**
	 * Tooltip/help dialog, displays the tooltip text (title) of a link in a styled dialog
	 */
	NS.Dialogs.register('help', NS.Class.extend(
		NS.Dialog,
		function(){
			this.content = $('p', this.container);
		},{
		activate:function(){
			var htmlTitle = this.origin.getAttribute('htmlTitle');
			
			if(htmlTitle){
				this.content.html(htmlTitle);
			}else{
				this.content.html(this.origin.getAttribute('title'));
			}
		}
	}));

	/**
	 * Ajax dialog, loads content from origin href, and sets title to either the origin's title 
	 * attribute or (when no title is found) the origin's text
	 */
	NS.Dialogs.register('ajax', NS.Class.extend(
		NS.Dialog,
		function() {
			this.content = $('.dialog-content', this.container);
			this.title = $('h2', this.container);
			NS.subscribe('submit', this.handleSubmit.bind(this));
		},{

		activate:function(toggle) {
			if(toggle) {
				var title = this.origin.title || $(this.origin).text();
				var href = this.origin.href;
				
				this.content.html('');
				this.title.html(title);
				
				NS.XHR.load(href, this.handleResponse.bind(this));
			}
		},

		write:function(html) {
			var target = this.responseTarget || this.content[0];
			NS.DOM.write(target, html);
			this.reflow();
		},

		handleSubmit:function(e) {
			var form = this.content.find('form')[0];
			if(form && e.target === form) {
				var node = null;
				var target = $(e.target).attr('ns:target');
				switch (target) {
					case 'top':
					case 'document':
						return; // post normally
					// break;
					case 'self':
						node = form;
					break;
					default:
						node = $(target)[0];
					break;
				}

				e.preventDefault();
				
				this.setResponseTarget(node);
				
				NS.XHR.sendForm(form, null, this.handleResponse.bind(this));
			}
		},

		submit:function() {
			var form = this.container.find('form')[0];
			NS.XHR.sendForm(form, null, this.handleResponse.bind(this));
		},

		setResponseTarget:function(node) {
			this.responseTarget = node;
		},

		handleResponse:function(response, status) {
			var html;
			if(!response || status >= 400) {
				// either an error occurred, or an unexpected response (non xml) was returned.
				var lang = NS.getLanguage();
				html = '<p>' + (/nl/i.test(lang)? Globals.MSG_SORRY : Globals.MSG_SORRY_EN) + '</p>' + 
					'<p class="actions"><a href="#" rel="dialog-close" class="button"><span>Sluiten</span></a></p>';
			} else {
				html = $(response).find('response').text();
			}
			
			this.write(html);
		}
	}));

	/**
	 * Print dialog
	 */
	NS.Dialogs.register('print', NS.Class.extend(
		NS.Dialog,
		function(){
			this.body = $('body');
			this.regDetail = /page-detail/;
			this.selection = $('#pmenu-selection');
			this.radios = this.selection.find('input');
			
			NS.relateLink(/print-/, this.handleClick.bind(this));
		},{

		activate:function(toggle) {
			var extended = this.origin.getAttribute('ns:extendedcontent');
			
			this.selection.toggleClass('disabled', !extended);
			this.radios.attr('disabled', !extended);
		},
		
		handleClick:function(link, rel) {
			var type = /print-([^ ]+)/i.exec(rel)[1];
			var form = $(link).closest('form');

			this.applyPreferences(form);
			switch (type) {
				case 'page': window.print(); break;
				case 'preview':
					this.previewWindow = window.open(
						NS.getProperty('URL_PRINT_PREVIEW')
					);
				break;
			}
			return true;
		},

		applyPreferences:function(form) {
			var regText = /^[a-z]/i;
			var regId = /print-([^ ]+)/i;
			var body = this.body;
			
			form.find('input:checkbox, input:radio').filter(':enabled').each(function(){
				var input = this, value = input.value, id = input.id;
				if(regId.test(id)) {
					var type = regId.exec(id)[1];
					switch (type) {
						case 'summary':  body.addClass('print-partly'); break;
						case 'extended': body.removeClass('print-partly'); break; 
						case 'pictures': body.toggleClass('print-noimages', !input.checked); break;
					}
				} else if(regText.test(value)) {
					body.toggleClass(value, input.checked);
				}
			});
		}
	}));

	/**
	 * Send a friend Dialog
	 */
	NS.Dialogs.register('emailmenu', NS.Class.extend(
		NS.Dialog,
		function(){
			this.form = this.container.find('form');
			this.selection = $('#emenu-selection');
			this.radios = this.selection.find('input');
			this.receiver = $('#emenu-receiver');
			NS.relateInput(/emenu-toself/, this.toggleFields.bind(this));
		},{

		activate:function(toggle) {
			var extended = this.origin.getAttribute('ns:extendedcontent');
			
			this.selection.toggleClass('disabled', !extended);
			this.radios.attr('disabled', !extended);
		},
		
		submit:function() {
			var form = this.form[0], action = form.action;
			if(form.responsetype) {
				form.responsetype.value = 'xml';
			}

			NS.XHR.sendForm(form, action, this.handleResponse.bind(this));
		},
		
		handleResponse:function(xml) {
			var thanks = $(xml).find('response').text();
			var sent = NS.Dialogs.open('emailsentmenu', this.origin);
			sent.write(thanks);
		},

		toggleFields:function(input) {
			this.receiver.toggleClass('disabled', input.checked);
			this.receiver.find('input:text').attr('disabled', input.checked);
		}
	}));

	/**
	 * Calendar dialog, used for selecting travel dates (future) and/or date of birth (past).
	 */
	var CalendarDialog = NS.Class.extend(
		NS.Dialog,
		
		function(){
			this.setupCalendar();
		},{

		setupCalendar: function() {
			var settings = { allowPast: /dialog-date|dialog-history/.test(this.container[0].id) };
			this.calendar = new NS.Calendar(this.container, settings);
			this.calendar.setAction(this.updateCalendar.bind(this));
		},

		activate:function(toggle) {
			if(toggle) {
				if(this.calendar.container != this.container) {
					delete this.calendar;
					this.setupCalendar();
				}

				var inputs = $('input', this.origin.parentNode);
				var i = Math.min(inputs.length, 4);
				var date = new Date(
					parseInt(inputs[i-1].value, 10),
					parseInt(inputs[i-2].value, 10)-1,
					parseInt(inputs[i-3].value, 10)
				);
				
				var start = this.origin.getAttribute('ns:startdate');
				var end = this.origin.getAttribute('ns:enddate');
				var allowed = this.origin.getAttribute('ns:alloweddates');
				
				date = date.getTime()? date : null;
				this.calendar.setDate(date, {
					startDate: start? new Date(parseInt(start, 10)) : false,
					endDate:   end? new Date(parseInt(end, 10)) : false,
					dates: 	   allowed? allowed : false 
				});
			}
		},

		updateCalendar:function(date, validate) {
			var inputs = $('input', this.origin.parentNode);
			var i = Math.min(inputs.length, 4);
			inputs[i-3].value = date.getDate();
			inputs[i-2].value = date.getMonth() + 1;
			inputs[i-1].value = date.getFullYear();
			NS.Dispatcher.fire('change', inputs[i-1]);
			setTimeout((function(){ 
				this.close();
			}).bind(this), 100);
		}
	});

	NS.Dialogs.register('calendar', CalendarDialog);
	NS.Dialogs.register('history', CalendarDialog);
	NS.Dialogs.register('date', CalendarDialog);

	NS.Dialogs.register('reisplanner', NS.Class.extend(
		NS.Dialog,
		function() {
			this.container.addClass('left');
			this.orientation = 'left center';
		}
	));
	
	/**
	 * Bind to NS.initialize
	 */
	NS.subscribe('initialize', function(){
		NS.Dialogs.initialize();
	});
});
