/**
 * Window.js
 *
 * Released under LGPL License.
 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
 *
 * License: http://www.tinymce.com/license
 * Contributing: http://www.tinymce.com/contributing
 */

/**
 * Creates a new window.
 *
 * @-x-less Window.less
 * @class tinymce.ui.Window
 * @extends tinymce.ui.FloatPanel
 */
define("tinymce/ui/Window", [
	"tinymce/ui/FloatPanel",
	"tinymce/ui/Panel",
	"tinymce/ui/DomUtils",
	"tinymce/dom/DomQuery",
	"tinymce/ui/DragHelper",
	"tinymce/ui/BoxUtils",
	"tinymce/Env",
	"tinymce/util/Delay"
], function(FloatPanel, Panel, DomUtils, $, DragHelper, BoxUtils, Env, Delay) {
	"use strict";

	var windows = [], oldMetaValue = '';

	function toggleFullScreenState(state) {
		var noScaleMetaValue = 'width=device-width,initial-scale=1.0,user-scalable=0,minimum-scale=1.0,maximum-scale=1.0',
			viewport = $("meta[name=viewport]")[0],
			contentValue;

		if (Env.overrideViewPort === false) {
			return;
		}

		if (!viewport) {
			viewport = document.createElement('meta');
			viewport.setAttribute('name', 'viewport');
			document.getElementsByTagName('head')[0].appendChild(viewport);
		}

		contentValue = viewport.getAttribute('content');
		if (contentValue && typeof oldMetaValue != 'undefined') {
			oldMetaValue = contentValue;
		}

		viewport.setAttribute('content', state ? noScaleMetaValue : oldMetaValue);
	}

	function toggleBodyFullScreenClasses(classPrefix) {
		for (var i = 0; i < windows.length; i++) {
			if (windows[i]._fullscreen) {
				return;
			}
		}

		$([document.documentElement, document.body]).removeClass(classPrefix + 'fullscreen');
	}

	function handleWindowResize() {
		if (!Env.desktop) {
			var lastSize = {
				w: window.innerWidth,
				h: window.innerHeight
			};

			Delay.setInterval(function() {
				var w = window.innerWidth,
					h = window.innerHeight;

				if (lastSize.w != w || lastSize.h != h) {
					lastSize = {
						w: w,
						h: h
					};

					$(window).trigger('resize');
				}
			}, 100);
		}

		function reposition() {
			var i, rect = DomUtils.getWindowSize(), layoutRect;

			for (i = 0; i < windows.length; i++) {
				layoutRect = windows[i].layoutRect();

				windows[i].moveTo(
					windows[i].settings.x || Math.max(0, rect.w / 2 - layoutRect.w / 2),
					windows[i].settings.y || Math.max(0, rect.h / 2 - layoutRect.h / 2)
				);
			}
		}

		$(window).on('resize', reposition);
	}

	var Window = FloatPanel.extend({
		modal: true,

		Defaults: {
			border: 1,
			layout: 'flex',
			containerCls: 'panel',
			role: 'dialog',
			callbacks: {
				submit: function() {
					this.fire('submit', {data: this.toJSON()});
				},

				close: function() {
					this.close();
				}
			}
		},

		/**
		 * Constructs a instance with the specified settings.
		 *
		 * @constructor
		 * @param {Object} settings Name/value object with settings.
		 */
		init: function(settings) {
			var self = this;

			self._super(settings);

			if (self.isRtl()) {
				self.classes.add('rtl');
			}

			self.classes.add('window');
			self.bodyClasses.add('window-body');
			self.state.set('fixed', true);

			// Create statusbar
			if (settings.buttons) {
				self.statusbar = new Panel({
					layout: 'flex',
					border: '1 0 0 0',
					spacing: 3,
					padding: 10,
					align: 'center',
					pack: self.isRtl() ? 'start' : 'end',
					defaults: {
						type: 'button'
					},
					items: settings.buttons
				});

				self.statusbar.classes.add('foot');
				self.statusbar.parent(self);
			}

			self.on('click', function(e) {
				var closeClass = self.classPrefix + 'close';

				if (DomUtils.hasClass(e.target, closeClass) || DomUtils.hasClass(e.target.parentNode, closeClass)) {
					self.close();
				}
			});

			self.on('cancel', function() {
				self.close();
			});

			self.aria('describedby', self.describedBy || self._id + '-none');
			self.aria('label', settings.title);
			self._fullscreen = false;
		},

		/**
		 * Recalculates the positions of the controls in the current container.
		 * This is invoked by the reflow method and shouldn't be called directly.
		 *
		 * @method recalc
		 */
		recalc: function() {
			var self = this, statusbar = self.statusbar, layoutRect, width, x, needsRecalc;

			if (self._fullscreen) {
				self.layoutRect(DomUtils.getWindowSize());
				self.layoutRect().contentH = self.layoutRect().innerH;
			}

			self._super();

			layoutRect = self.layoutRect();

			// Resize window based on title width
			if (self.settings.title && !self._fullscreen) {
				width = layoutRect.headerW;
				if (width > layoutRect.w) {
					x = layoutRect.x - Math.max(0, width / 2);
					self.layoutRect({w: width, x: x});
					needsRecalc = true;
				}
			}

			// Resize window based on statusbar width
			if (statusbar) {
				statusbar.layoutRect({w: self.layoutRect().innerW}).recalc();

				width = statusbar.layoutRect().minW + layoutRect.deltaW;
				if (width > layoutRect.w) {
					x = layoutRect.x - Math.max(0, width - layoutRect.w);
					self.layoutRect({w: width, x: x});
					needsRecalc = true;
				}
			}

			// Recalc body and disable auto resize
			if (needsRecalc) {
				self.recalc();
			}
		},

		/**
		 * Initializes the current controls layout rect.
		 * This will be executed by the layout managers to determine the
		 * default minWidth/minHeight etc.
		 *
		 * @method initLayoutRect
		 * @return {Object} Layout rect instance.
		 */
		initLayoutRect: function() {
			var self = this, layoutRect = self._super(), deltaH = 0, headEl;

			// Reserve vertical space for title
			if (self.settings.title && !self._fullscreen) {
				headEl = self.getEl('head');

				var size = DomUtils.getSize(headEl);

				layoutRect.headerW = size.width;
				layoutRect.headerH = size.height;

				deltaH += layoutRect.headerH;
			}

			// Reserve vertical space for statusbar
			if (self.statusbar) {
				deltaH += self.statusbar.layoutRect().h;
			}

			layoutRect.deltaH += deltaH;
			layoutRect.minH += deltaH;
			//layoutRect.innerH -= deltaH;
			layoutRect.h += deltaH;

			var rect = DomUtils.getWindowSize();

			layoutRect.x = self.settings.x || Math.max(0, rect.w / 2 - layoutRect.w / 2);
			layoutRect.y = self.settings.y || Math.max(0, rect.h / 2 - layoutRect.h / 2);

			return layoutRect;
		},

		/**
		 * Renders the control as a HTML string.
		 *
		 * @method renderHtml
		 * @return {String} HTML representing the control.
		 */
		renderHtml: function() {
			var self = this, layout = self._layout, id = self._id, prefix = self.classPrefix;
			var settings = self.settings, headerHtml = '', footerHtml = '', html = settings.html;

			self.preRender();
			layout.preRender(self);

			if (settings.title) {
				headerHtml = (
					'<div id="' + id + '-head" class="' + prefix + 'window-head">' +
						'<div id="' + id + '-title" class="' + prefix + 'title">' + self.encode(settings.title) + '</div>' +
						'<div id="' + id + '-dragh" class="' + prefix + 'dragh"></div>' +
						'<button type="button" class="' + prefix + 'close" aria-hidden="true">' +
							'<i class="mce-ico mce-i-remove"></i>' +
						'</button>' +
					'</div>'
				);
			}

			if (settings.url) {
				html = '<iframe src="' + settings.url + '" tabindex="-1"></iframe>';
			}

			if (typeof html == "undefined") {
				html = layout.renderHtml(self);
			}

			if (self.statusbar) {
				footerHtml = self.statusbar.renderHtml();
			}

			return (
				'<div id="' + id + '" class="' + self.classes + '" hidefocus="1">' +
					'<div class="' + self.classPrefix + 'reset" role="application">' +
						headerHtml +
						'<div id="' + id + '-body" class="' + self.bodyClasses + '">' +
							html +
						'</div>' +
						footerHtml +
					'</div>' +
				'</div>'
			);
		},

		/**
		 * Switches the window fullscreen mode.
		 *
		 * @method fullscreen
		 * @param {Boolean} state True/false state.
		 * @return {tinymce.ui.Window} Current window instance.
		 */
		fullscreen: function(state) {
			var self = this, documentElement = document.documentElement, slowRendering, prefix = self.classPrefix, layoutRect;

			if (state != self._fullscreen) {
				$(window).on('resize', function() {
					var time;

					if (self._fullscreen) {
						// Time the layout time if it's to slow use a timeout to not hog the CPU
						if (!slowRendering) {
							time = new Date().getTime();

							var rect = DomUtils.getWindowSize();
							self.moveTo(0, 0).resizeTo(rect.w, rect.h);

							if ((new Date().getTime()) - time > 50) {
								slowRendering = true;
							}
						} else {
							if (!self._timer) {
								self._timer = Delay.setTimeout(function() {
									var rect = DomUtils.getWindowSize();
									self.moveTo(0, 0).resizeTo(rect.w, rect.h);

									self._timer = 0;
								}, 50);
							}
						}
					}
				});

				layoutRect = self.layoutRect();
				self._fullscreen = state;

				if (!state) {
					self.borderBox = BoxUtils.parseBox(self.settings.border);
					self.getEl('head').style.display = '';
					layoutRect.deltaH += layoutRect.headerH;
					$([documentElement, document.body]).removeClass(prefix + 'fullscreen');
					self.classes.remove('fullscreen');
					self.moveTo(self._initial.x, self._initial.y).resizeTo(self._initial.w, self._initial.h);
				} else {
					self._initial = {x: layoutRect.x, y: layoutRect.y, w: layoutRect.w, h: layoutRect.h};

					self.borderBox = BoxUtils.parseBox('0');
					self.getEl('head').style.display = 'none';
					layoutRect.deltaH -= layoutRect.headerH + 2;
					$([documentElement, document.body]).addClass(prefix + 'fullscreen');
					self.classes.add('fullscreen');

					var rect = DomUtils.getWindowSize();
					self.moveTo(0, 0).resizeTo(rect.w, rect.h);
				}
			}

			return self.reflow();
		},

		/**
		 * Called after the control has been rendered.
		 *
		 * @method postRender
		 */
		postRender: function() {
			var self = this, startPos;

			setTimeout(function() {
				self.classes.add('in');
				self.fire('open');
			}, 0);

			self._super();

			if (self.statusbar) {
				self.statusbar.postRender();
			}

			self.focus();

			this.dragHelper = new DragHelper(self._id + '-dragh', {
				start: function() {
					startPos = {
						x: self.layoutRect().x,
						y: self.layoutRect().y
					};
				},

				drag: function(e) {
					self.moveTo(startPos.x + e.deltaX, startPos.y + e.deltaY);
				}
			});

			self.on('submit', function(e) {
				if (!e.isDefaultPrevented()) {
					self.close();
				}
			});

			windows.push(self);
			toggleFullScreenState(true);
		},

		/**
		 * Fires a submit event with the serialized form.
		 *
		 * @method submit
		 * @return {Object} Event arguments object.
		 */
		submit: function() {
			return this.fire('submit', {data: this.toJSON()});
		},

		/**
		 * Removes the current control from DOM and from UI collections.
		 *
		 * @method remove
		 * @return {tinymce.ui.Control} Current control instance.
		 */
		remove: function() {
			var self = this, i;

			self.dragHelper.destroy();
			self._super();

			if (self.statusbar) {
				this.statusbar.remove();
			}

			i = windows.length;
			while (i--) {
				if (windows[i] === self) {
					windows.splice(i, 1);
				}
			}

			toggleFullScreenState(windows.length > 0);
			toggleBodyFullScreenClasses(self.classPrefix);
		},

		/**
		 * Returns the contentWindow object of the iframe if it exists.
		 *
		 * @method getContentWindow
		 * @return {Window} window object or null.
		 */
		getContentWindow: function() {
			var ifr = this.getEl().getElementsByTagName('iframe')[0];
			return ifr ? ifr.contentWindow : null;
		}
	});

	handleWindowResize();

	return Window;
});