define(['scripts/knockout-3.3.0', 'mod/properties/propertyutils', "scripts/ACTUtils",'jqx/jqxtooltip'], function(ko, PropertyUtils, ACTUtils) {

	function PropertyCaptionViewModel(params, element) {
		var self = params;

		self.utils = new PropertyUtils();
		self.utils.Init(self, params, element);
		$(element).addClass("propertyContainerGroup");
		self.element = element;
		self.properties = ko.observableArray(params.Properties);
		self.collapsed = ko.observable(false);

		self.$collapseButton = $(element).find(".collapseButton").first();
		self.$caption = $(element).find(".caption").first();
		self.$properties = $(element).find(".propertiesContainer");

		// don't show root
		if (params.parentGroup == null) {
			self.$collapseButton.remove();
			self.$caption.remove();
		} else {
			$(element).addClass("innerGroup");
		}

		var doCollapse = function() {
			if (self.$properties.is(":visible")) {
				self.collapsed(true);
				self.$properties.slideUp(200);
			} else {
				self.collapsed(false);
				self.$properties.slideDown(200);
			}
		}
		self.$collapseButton.on("click", doCollapse);
		self.$caption.on("click", doCollapse);

		if (params.Attributes && params.Attributes["collapse"]) {
			self.collapsed(true);
			self.$properties.hide();
		}

		for (var key in params.properties()) {
			prop = self.properties()[key];
			prop.optionValues = ko.observableArray([]);
			if (prop.Component=="property-select" || prop.Component === "property-selectapply" || prop.Component == "property-radio") {
				prop.optionValues(prop.Options);
			}
			prop.ready = ko.observable(false);
			prop.ready.subscribe(function(newVal) {
				var ref = true;
				for (var key in self.properties()) {
					prop = self.properties()[key];
					if (prop.ready()==false) {
						ref = false;
					}
				}
				if (ref)
					self.utils.Ready(params);
			}.bind(self));

			prop.parent = params.parent;
			prop.parentGroup = params;

			params.parent.base.addCallbackX(prop, "onchange", "ValueChanged");
			params.parent.base.addCallbackX(prop, "onclick", "ButtonClicked");
			params.parent.base.addCallbackX(prop, "onrightclick", "RightClicked");
		}

		changeLabelsWidth = function($labels, w) {
			$labels.each(function() { $(this).css("width", w); });
		}

		params.updateProperties = function(obj) {
			self.firstInit = true;

			for (var key in this.properties()) {
				var prop = this.properties()[key];
				var propObj = obj.Properties[key];

				if (prop.Component == "property-caption") {
					prop.updateProperties(propObj);
					continue;
				}

				// to avoid update property value with displayValue
				prop.updating = true;

				if (propObj.Component == "property-quantity") {
					prop.unit(propObj.Unit);
				}
				if (propObj.Component == "property-select") {
					var needDomUpdate = prop.optionValues().length == 0 && propObj.Options.length > 0;
					prop.optionValues(propObj.Options);
					if (needDomUpdate) {
						// update DOM element if not sometimes new options doesn't show up
						$(prop.element).hide();
						setTimeout(function(prop) {
							$(prop.element).show();
						}, 0, prop);
					}
				}

				if (prop.visible) { // if not the property is not fully initialized yet so we don't need to do the following
					prop.visible(propObj.IsVisible);
					prop.value(propObj.DisplayValue);
					prop.stateMsg(propObj.StateMessage);
					prop.updateStateMessageColor(propObj.StateMessageType);
					prop.active(propObj.IsActive);
					prop.buttons(propObj.Buttons);
				}

				prop.updating = false;
			}
		}

		params.resize = function(height, width) {
			var $element = $(self.element);
			var width = $element.width();

			var $propertiesContainer = $element.find(".propertiesContainer");

			// we want our direct children, not all (in case of property group into another)
			var $propertyLines = $element
				.children(".propertyLine")
				.children(".propertiesContainer")
				.children(".propertyContainer")
				.children(".propertyLine");
			var $control = $propertyLines.children(".controlmessage");
			var $label   = $propertyLines.children(".label");
			var $buttons = $propertyLines.children(".buttons");
			var $helps   = $propertyLines.children(".help");

			// sometimes properties aren't ready to be resize !
			if($propertyLines.length === 0 && self.properties().length !== 0) {
				setTimeout(function() {params.resize(height, width);}, 0);
				return;
			};

			// compute the width of the largest label
			changeLabelsWidth($label, "");  // need to reset the label size
			var labelMaxWidth = 0;
			var propHaveHelp = false;
			for (var key in self.properties()) {
				prop = self.properties()[key];
				if (prop.Component=="property-caption") {
					prop.resize(height, width);
					continue;
				}

				var $propLabel = $(prop.element).find(".label");
				var labelWidth = $propLabel.width();
				prop.oldLabelWidth = labelWidth;  // keep it for tooltip
				if (labelWidth > labelMaxWidth)
					labelMaxWidth = labelWidth;

				if (!ACTUtils.StringIsNullOrEmpty(prop.Help))
					propHaveHelp = true;

				// destroy all tooltip (will be reput later if needed)
				ACTUtils.DestroyTooltip($propLabel);
			}

			var overflows = ACTUtils.GetOverflowsAndSetThemTo($propertiesContainer, "hidden");

			// apply width and put tooltips on label that are crop
			if (self.parent.oneLine) changeLabelsWidth($label, labelMaxWidth);
			else                     changeLabelsWidth($label, "");

			var w = labelMaxWidth;
			var m = width;
			// we try to avoid to have a control tinier than the label by forcing them to have approximatly the same size
			if (m-w-60<w) {
				w = (m-60)/2;
				if (self.parent.oneLine) $label.outerWidth(w);

				for (var key in self.properties()) {
					prop = self.properties()[key];
					if (prop.Component=="property-caption")
						continue;

					var $propLabel = $(prop.element).find(".label");
					if (self.parent.oneLine && prop.oldLabelWidth > w) {
						ACTUtils.CreateTooltip($propLabel, prop.Caption, true, {position:"bottom"});
					}
				}
			}

			// compute button width
			var maxButtonWidth = 0;
			for (var buttonsIndex = 0; buttonsIndex < $buttons.length; buttonsIndex++) {
				var $buttonsChildren = $($buttons[buttonsIndex]).children();
					var buttonWidth = 0;
				for (var buttonChildIndex = 0; buttonChildIndex < $buttonsChildren.length; buttonChildIndex++)
					buttonWidth += $($buttonsChildren[buttonChildIndex]).outerWidth(true);
				if (buttonWidth > maxButtonWidth)
					maxButtonWidth = buttonWidth;
			}

			// compute control width
			var controlWidth = 0;

			var margin = 30;
			if (propHaveHelp)
				margin += 30;

			if (self.parent.oneLine)
				controlWidth = m-w-margin;
			else
				controlWidth = m-margin;
			controlWidth = controlWidth - maxButtonWidth;

			$control.outerWidth(controlWidth);

			// because of css style (max-width) the outerWidth could not be the same as what we put earlier
			controlWidth = $control.outerWidth();

			// positionate the help button
			var leftHelp = controlWidth + 15;
			if (self.parent.oneLine) {
				leftHelp += $label.outerWidth();
				leftHelp += maxButtonWidth;
			}
			$helps.css("left", leftHelp);

			// callbacks
			for (var key in self.properties()) {
				prop = self.properties()[key];
				if (prop.onResize)
					prop.onResize();
			}

			ACTUtils.SetOverflows($propertiesContainer, overflows);
		}

		params.triggerAction = function(obj) {
			for (var i = 0; i < self.properties().length; i++) {
				var thisProp = self.properties()[i];
				var objProp  = obj.Properties[i];

				if (thisProp.Component == "property-caption") {
					thisProp.triggerAction(objProp)
				}

				if (objProp.TriggerValueChange != null) {
					thisProp.triggerValueChange(objProp.TriggerValueChange);
				}
				if (objProp.TriggerButtonClick != null) {
					thisProp.triggerButtonClick(objProp.TriggerButtonClick);
				}
			}
		}

		params.onShow = function() {
			/*
				fix bug where properties with control=select won't set to null
				when created hidden then showed to fix that we update the value
				of the select property to null
			*/
			for (var index = 0; index < this.properties().length; ++index) {
				prop = this.properties()[index];

				if (prop.Control == "property-caption") {
					prop.onShow();
					continue;
				}

				if (typeof(prop.value) === "undefined")
					continue;

				var value = prop.value();
				if (prop.Control != "select" || (value != null && value != ""))
					continue;

				var $element = $(self.element);
				$control = $($element.find(".control")[index]).find("select");
				$control.val("");
			}
		}

		params.updateStyle = function() {
			for (var index = 0; index < this.properties().length; ++index) {
				prop = this.properties()[index];
				if (prop.updateStyle)
					prop.updateStyle();
			}
		}

		if (self.Properties.length == 0)
			self.utils.Ready(params);

		return self;
	}

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

});
