define(
	['scripts/knockout-3.3.0','scripts/ACTComponent.js', 'scripts/ACTUtils', 'jqx/jqxwindow'],
	function(ko, ACTComponent, ACTUtils, jqxwindow)
{
	function DialogComponentViewModel(params, element) {
		var self = this;

		self.base = new ACTComponent($(element), params);
		self.baseModel = params.baseModel;
		self.visible = false;
		self.closeWhenClickOutside = false;
		self.keepDimension = false;

		var $jqxDialog = $(element).find(".jqxwindow");
		var $jqxRealDialog = null;

		self.components = ko.observableArray([]);
		self.componentModels = {}
		self.readyChildrenCount = 0;
		self.position = "center";
		self.firstTimeOpen = true;
		self.updatePositionAfterOpen = true;
		self.showHeader = true;

		this.baseModel.handleComponentSize = false;
		// for some reasons, the size is already calculated so we need to reset it to 0.
		this.baseModel.w(0);
		this.baseModel.h(0);

		self.updatePosition = function(dialogWidth, dialogHeight) {
			var newWidth = self.setWidth(dialogWidth);
			var newHeight = self.setHeight(dialogHeight);

			var windowWidth = $(window).outerWidth();
			var windowHeight = $(window).outerHeight();

			if (!self.keepPosition || self.firstTimeOpen || self.positionChange) {
				var position = [];
				if (typeof self.position != "string") {  // absolute positioning
					if (self.alignement == "TopLeft") {
						position[0] = self.position[0];
						position[1] = self.position[1];
					} else  if (self.alignement == "Center") {
						position[0] = self.position[0] - newWidth * 0.5;
						position[1] = self.position[1] - newHeight * 0.5;
					} else  if (self.alignement == "TopRight") {
						position[0] = self.position[0] - newWidth;
						position[1] = self.position[1];
					} else  if (self.alignement == "BottomRight") {
						position[0] = self.position[0] - newWidth;
						position[1] = self.position[1] - newHeight;
					} else  if (self.alignement == "BottomLeft") {
						position[0] = self.position[0];
						position[1] = self.position[1] - newHeight;
					}
				} else {
					position = self.position;
				}

				position[0] = ACTUtils.Clamp(position[0], 0, windowWidth  - newWidth);
				position[1] = ACTUtils.Clamp(position[1], 0, windowHeight - newHeight);

				// reset the positon
				$jqxDialog.jqxWindow({position: position});
				self.positionChange = false;
			}
		}

		self.getWidth = function() {
			return $jqxDialog.jqxWindow("width");
		}

		self.setWidth = function(w) {
			// limitate size of the dialog at 90% of the window size (before set the position in case of "center" !)
			var ratioWindowMax = 0.9;
			var windowWidth = $(window).outerWidth();
			var maxWidth = windowWidth * ratioWindowMax;
			if (w > maxWidth)
				w = maxWidth;

			$jqxDialog.jqxWindow("width", w);
			return w;
		}

		self.getHeight = function() {
			return $jqxDialog.jqxWindow("height");
		}

		self.setHeight = function(h) {
			var ratioWindowMax = 0.9;
			var windowHeight = $(window).outerHeight();
			var maxHeight = windowHeight * ratioWindowMax;
			if (h > maxHeight)
				h = maxHeight;

			$jqxDialog.jqxWindow("height", h);
			return h;
		}

		self.updateSize = function() {
			var change = false;

			var width  = self.baseModel.getW();
			var height = self.baseModel.getH();
			if (self.firstTimeOpen || (!self.keepDimension && (width != self.width || height != self.height))) {
				var headerHeight = self.$jqxRealDialog.find(".dialogHeader").outerHeight(true);
				var paddingHeight = self.showHeader ? 10 : 0;

				// cause the height of the window take the header height in it
				var dialogHeight = height + headerHeight + paddingHeight;

				self.width = width;
				self.height = dialogHeight;

				if (self.updateSizeTimeout)
					clearTimeout(self.handler);

				// in case the component need the dialog to be open. 
				// But we don't want to change the position of the dialog each time the dialog change size
				if (self.updatePositionAfterOpen) {
					self.updatePositionAfterOpen = false;
					// update position to recenter, limitate size etc... need to be done asap. So we do it before the timeout
					self.updatePosition(self.width, self.height);
				}

				// timeout cause animation remove reset initial dimentions
				self.updateSizeTimeout = setTimeout(function() {
					var $contentElements = $jqxDialog.find(".content");
					var oldOverflowState = ACTUtils.GetOverflowsAndSetThemTo($contentElements, "hidden");

					self.setHeight(dialogHeight);
					self.setWidth(self.width);

					ACTUtils.SetOverflows($contentElements, oldOverflowState);
					delete self.updateSizeTimeout;
				}, 0);

				// also here cause if position="center" the dialog need to know the size before opening
				self.setHeight(dialogHeight);
				self.setWidth(self.width);

				change = true;
			}
		}

		self.resizeChildren = function(dontUpdateSize) {
			ACTUtils.Log("start resize dialog's children", 1);
			var $contentElements = $jqxDialog.find(".content");
			var oldOverflowState = ACTUtils.GetOverflowsAndSetThemTo($contentElements, "hidden");
			for (var key in self.componentModels) {
				ACTUtils.Log("set dirty: "+key, 1);
				self.componentModels[key].setDirty();
			}
			for (var key in self.componentModels) {
				ACTUtils.Log("resize component: "+key, 1);
				self.componentModels[key].resize();
			}

			if (!dontUpdateSize) {
				self.baseModel.setDirty(); // since we resize our children our size may change
				self.updateSize();
			}

			ACTUtils.SetOverflows($contentElements, oldOverflowState);
		}

		self.registerComponentModel = function(name, component) {
			self.componentModels[name] = component;
		}

		self.componentReady = function(component, newVal) {
			/* (msaul 2016/06/07)
			 * We could change our ready status here but that would imply a resize
			 * of all components under our parent, which is not needed since we are a dialog.
			 * When I wrote that comments no advandage at change this status.
			 */
			if (newVal)
				self.readyChildrenCount++;
			else
				self.readyChildrenCount--;

			if (self.readyChildrenCount == self.components().length) {
				if (self.visible && !self.isOpen()) {
					self.OpenDialog();
				} else {
					self.resizeChildren();
				}
			}

			if (self.readyChildrenCount > self.components().length)
				console.warn("Too much component are ready. That sounldn't suppose to happen. The same component may exist multiple time that can come from Model.Id");
		}

		params.baseModel.disposeFunctions.push(function() {
			$jqxDialog.jqxWindow("destroy");
		});

		params.baseModel.lockFunctions.push(function(lockKey, noLockPaths) {
			for (var key in self.componentModels) {
				self.componentModels[key].lock(lockKey, noLockPaths);
			}
		});

		params.baseModel.unlockFunctions.push(function(lockKey) {
			for (var key in self.componentModels) {
				self.componentModels[key].unlock(lockKey);
			}
		});

		self.base.onRefresh = function(obj) {
			// components (like panel)
			if (!ACTUtils.ListOfComponentAreEqual(self.components(), obj.Components)) {
				self.componentModels = {}
				self.readyChildrenCount = 0;
				var components = obj.Components;
				for (var i=0;i<components.length;i++) {
					var c = components[i];
					c.gridPanel = self;
				}
				self.components(components);
				//params.ready(false);
			}

			if (self.components().length == 1 &&
				((self.components()[0].HeightType == "Percentage" && self.baseModel.definition().HeightType == "FitToContent")
			  || (self.components()[0].WidthType  == "Percentage" && self.baseModel.definition().WidthType  == "FitToContent")))
				console.warn("Dialog component in FitToContent with child in percentage can't work correctly. Put the child in FitToContent.");

			self.closeWhenClickOutside = obj.CloseWhenClickOutside;

			var newPosition = obj.PositionStr || obj.PositionCoordinate;
			if (newPosition != self.position) {
				self.position = newPosition;
				self.positionChange = true;
			}

			self.alignement = obj.Alignement;
			self.keepPosition = obj.KeepPosition;
			self.keepDimension = obj.KeepDimension;

			self.OnShow = obj.OnShow;
			self.OnHide = obj.OnHide;

			self.base.addCallback(self, "onShowDialog", "OnShow", []);
			self.base.addCallback(self, "onHideDialog", "OnHide", []);

			// create dialog (setTimeout cause we want to do it after the knockout data-bind)
			setTimeout(function() {

				// a new jqx-resize is created when we change resizable, draggable, etc... we have to kill them all before adding new ones (it's a jqx bug)
				var $resizeArmy = $jqxDialog.find(".jqx-resize");
				if ($resizeArmy.length > 0) {
					var $content = $($resizeArmy[$resizeArmy.length - 1]).children();
					var $firstResize = $($resizeArmy[0]);
					$firstResize.replaceWith($content);
				}

				$jqxDialog.jqxWindow({
					autoOpen           : false,
					isModal            : true,
					keyboardCloseKey   : '',
					animationType      : 'none',
					resizable          : obj.Resizable,
					showCloseButton    : obj.Closable,
					showCollapseButton : obj.Collapsable,
					draggable          : obj.Draggable,
					isModal            : obj.Modal,
					animationType      : obj.Animation.toLowerCase(),
					minWidth           : 0,
					minHeight          : 0,
				});

				if (self.$jqxRealDialog == null)
				{
					self.$jqxRealDialog = $(".jqx-window").last();
					self.baseModel.$realElement = self.$jqxRealDialog;
					self.baseModel.$realElement.css("opacity", obj.Opacity);
				}

				// we need to do that after setting realElement
				self.baseModel.setDirty();
				self.updateSize();

				var title = "";
				var paddingLeftText = 0;
				if (obj.Icon && obj.Icon != "") {
					title += "<img src='" + obj.Icon + "' style='width:16px; height:16px; position:absolute; left:5px;right:5px;'/>";
					paddingLeftText = 21;
				}
				title += "<span style='line-height: 16px; padding-left: " + paddingLeftText + "px;'>" + obj.Title + "</span>";
				$jqxDialog.jqxWindow("setTitle", title);

				self.showHeader = obj.ShowHeader;
				if (!self.showHeader) {
					$jqxDialog.addClass("noheader");
				} else {
					$jqxDialog.removeClass("noheader");
				}
				$jqxDialog.addClass("actDefault");

				if (self.visible != obj.Visible) {
					if (obj.Visible) {
						if (params.ready()) {
							self.OpenDialog();
						}
					} else {
						$jqxDialog.jqxWindow("close");
					}
				}

				self.visible = obj.Visible;
			}, 0);
			params.ready(true);
		}

		$jqxDialog.on('open', function (event) {
			self.onShowDialog();
			self.visible = true;

			for (var key in self.componentModels) {
				self.componentModels[key].callShowFunctions();
			}
		});

		$jqxDialog.on('close', function (event) {
			self.onHideDialog();
			self.visible = false;

			for (var key in self.componentModels) {
				self.componentModels[key].callHideFunctions();
			}
		});

		$jqxDialog.on('resizing', function (event) {
			self.resizeChildren(true);
			self.width = self.getWidth();
			self.height = self.getHeight();
		});

		$(window).on("resize", function(event) {
			// to not lose the dialog when resizing the window
			var left   = $jqxDialog.position().left;
			var top    = $jqxDialog.position().top;
			var width  = self.getWidth();
			var height = self.getHeight();

			var $window = $(window);
			var windowWidth = $window.outerWidth();
			var windowHeight = $window.outerHeight();

			var newLeft = left;
			if (left + width > windowWidth)
			{
				newLeft = windowWidth - width;
				if (newLeft < 0) {
					self.setWidth(width + newLeft);
					newLeft = 0;
				}
			}

			var newTop = top;
			if (top + height > windowHeight)
			{
				newTop = windowHeight - height;
				if (newTop < 0) {
					self.setHeight(height + newTop);
					newTop = 0;
				}
			}

			if (newLeft != left || newTop != top)
				$jqxDialog.jqxWindow("move", newLeft, newTop);
		});

		self.isOpen = function() {
			return $jqxDialog.jqxWindow("isOpen");
		}

		self.OpenDialog = function() {
			if (self.isOpen())
				return;

			if ($jqxDialog.jqxWindow("collapsed"))
				$jqxDialog.jqxWindow("collapsed", false);

			self.updatePosition(self.width, self.height);

			$jqxDialog.jqxWindow("open");
			self.firstTimeOpen = false;
			self.updatePositionAfterOpen = true;

			// wait for the window to be open to resize the dialog
			setTimeout(function() {
				self.resizeChildren();
			}, 0);
		}

		// close dialog if we click outside the window
		self.OnClickWindow = function(event) {
			if (!self.closeWhenClickOutside)
				return;

			var isJqxWindow = $(event.target).hasClass("jqx-window");
			var isJqxWindowChildren = $(event.target).parents(".jqx-window").length > 0;
			if ($jqxDialog.jqxWindow("isOpen") && !isJqxWindow && !isJqxWindowChildren) {
				$jqxDialog.jqxWindow('close');
			}
		}

		// Not in capture phase cause we want to know if the dialog was open when we catch a click on a button
		// to toggle the visibility for example
		window.addEventListener("click", self.OnClickWindow, false);
		window.addEventListener("resize", self.OnClickWindow, false);

		self.base.initialize();
	}

	return {
		createViewModel: function(params, componentInfo) {
			return new DialogComponentViewModel(params, componentInfo.element);
		}
	};

});

