define(['scripts/knockout-3.3.0','scripts/ACTComponent.js', "scripts/ACTUtils",'jqx/jqxtooltip'], function(ko, ACTComponent, ACTUtils) {

	function PropertiesComponentViewModel(params, element) {
		var self = this;

		self.base = new ACTComponent($(element), params);
		self.rootProperty = ko.observable();
		self.params = params;
		self.element = element;
		self.$element = $(element);

		self.lastId = -1;

		decimalSeparator = params.Model.DecimalSeparator;

		self.oneLine = false;

		self.ResetFocusVariable = function() {
			self.wantToGoNext = false;
			self.indexFocusedElement = 0;
			self.oldValue = null;
			self.$lastFocusableOn110 = null;
		}

		self.ResetFocusVariable();

		self.GetFocusables = function(element) {
			return $(element).find('input,a,select,textarea');
		}

		self.ChangeFocusOnEnter = function(element, changeFocus) {
			var focusable = self.GetFocusables(element);

			var nextIndex = self.indexFocusedElement;
			if (changeFocus) {
				nextIndex = nextIndex + 1;
				while (nextIndex != self.indexFocusedElement &&
				      (!focusable.eq(nextIndex).is(":visible") ||
				       focusable.eq(nextIndex).is("[disabled]") ||
				       (focusable.eq(nextIndex).is("[readonly]") && !focusable.eq(nextIndex).is(".select"))))  // select are readonly
				{
					nextIndex++;
					if (nextIndex > focusable.length)
						nextIndex = 0;
				}
			}

			next = focusable.eq(nextIndex);
			next.focus();
		}

		var triggerOnChange = function(event) {
			ko.dataFor(event.target).triggerValueChange(event.target.value);
			$(event.target).unbind("focusout", triggerOnChange);
		}

		ko.bindingHandlers.keyPressed = {
			init: function(element, valueAccessor, allBindingsAccessor) {
				$(element).on('keydown', 'input,select', function (e) {
					if (e.keyCode != 13 && e.keyCode != 9 && self.$lastFocusableOn110) {
						self.$lastFocusableOn110.unbind("focusout", triggerOnChange);
					}

					var focusable = self.GetFocusables(element);
					self.indexFocusedElement = focusable.index(this);

					if (e.keyCode == 110) {  // pav num dot
						if (!focusable.is("[readonly]"))
						{
							var inputElement = focusable[self.indexFocusedElement];
							ACTUtils.OnKeyPressedReplace(inputElement, e, decimalSeparator);
							$(inputElement).on("focusout", triggerOnChange);  // cause the previous line remove the on change event
							self.$lastFocusableOn110 = $(inputElement);
							e.preventDefault();
							return false;
						}
					} else if (e.keyCode == 13 || e.keyCode == 9) {  // enter
						if (self.oldValue == null || focusable[self.indexFocusedElement].value == self.oldValue) {
							self.ChangeFocusOnEnter(element, true);
							self.oldValue = null;
						} else {
							self.wantToGoNext = true;
							this.blur();
						}
						e.preventDefault();
						return false;
					} else {
						if (self.oldValue === null) {
							self.oldValue = focusable[self.indexFocusedElement].value;
						}
					}

					return true;
				});
			}
		}

		self.updateOneMultipleLineClass = function($element) {
			if (self.oneLine) {
				$element.addClass("oneline");
				$element.removeClass("multipleline");
			} else {
				$element.addClass("multipleline");
				$element.removeClass("oneline");
			}
		}

		self.base.onRefresh = function(obj) {
			if (obj.Command == "TriggerAction") {
				self.rootProperty().triggerAction(obj.RootProperty);
				return;
			}
			decimalSeparator = obj.DecimalSeparator;

			var newId = obj.PropertyId;

			if (newId != self.lastId) {
				self.lastId = newId;

				self.oneLine = obj.OneLine;
				self.updateOneMultipleLineClass($(self.element).find(".propertiesComp"));
				self.disabled = obj.Disabled;

				self.params.ready(false);
				self.rootProperty(obj.RootProperty);
				var firstFocusDone = false;

				self.rootProperty().parent = self;
				self.rootProperty().ready = ko.observable(false);
				self.rootProperty().ready.subscribe(function(newVal) {
					// need setTimeout to let the time for the last property's DOM to be updated
					setTimeout(function() {
						// update styles
						var $labels = self.$element.find(".label");
						$labels.css("color", obj.LabelColor || "");

						var $controls = self.$element.find("input, select");
						var controlH = obj.ControlsHeight;
						$controls.outerHeight(controlH);

						var $checkboxes = self.$element.find(".boolean-checkbox");
						var checkboxDim = controlH - 7;  // controlH to big
						$checkboxes.height(checkboxDim);
						$checkboxes.width(checkboxDim);  // weird if not the same dim for width and height

						if (self.oneLine) {
							$labels.height(controlH);
							$labels.css("line-height", controlH + "px");
						}
						var $buttons = self.$element.find("button");
						$buttons.outerHeight(controlH);

						// disabled
						if (self.disabled) {
							var $buttons = self.$element.find("button, input");
							$buttons.attr("disabled", "disabled");

							var stopEvent = function(e) {
								var $target = $(e.toElement);
								if (!$target.is(".jqxSlider") && $target.parents(".jqxSlider").length == 0) {
									return;
								}
								e.stopPropagation();
								return false;
							};

							var $propertyLines = self.$element.find(".propertyLine");
							for (var i = 0; i < $propertyLines.length; i++)
							{
								var propertyLine = $propertyLines[i];
								propertyLine.addEventListener("mousedown", stopEvent, true);
								propertyLine.addEventListener("mouseup", stopEvent, true);
								propertyLine.addEventListener("focus", stopEvent, true);
								propertyLine.addEventListener("click", stopEvent, true);
							}
						}

						self.rootProperty().updateStyle();

						// disable automatic focus when loading a page. Otherwise the first input of the page is focused.
						if (!obj.Autofocus)
							self.$element.find('input').blur();

						// then ready
						self.params.ready(true);
					}, 0);

					if (!firstFocusDone) {
						var focusable = self.GetFocusables(self.element);
						if (focusable.length > 0) {
							var wantedElement = focusable[0];
							wantedElement.focus();
							firstFocusDone = true;
						}
					}
				});
				self.ResetFocusVariable();
			} else {
				var scrollInfo = ACTUtils.GetScrollPosition($(self.element));

				var wasReady = self.params.ready();
				if (wasReady) {
					self.params.ready(false);
				}

				var previousValue = null;
				var focusable = self.GetFocusables(self.element);
				if (focusable[self.indexFocusedElement]) {
					previousValue = focusable[self.indexFocusedElement].value;
				}

				if (wasReady) // if wasn't ready the function updateProperties isn't set yet
					self.rootProperty().updateProperties(obj.RootProperty);

				if (self.wantToGoNext) {
					var valueWasCorrect = focusable[self.indexFocusedElement].value == previousValue;
					self.ChangeFocusOnEnter(self.element, valueWasCorrect);
					self.wantToGoNext = false;
				}
				self.oldValue = null;
				if (wasReady)
					self.params.ready(true);

				if (self.askForResize) {
					self.rootProperty().resize(self.askForResize.height, self.askForResize.width);
					delete self.askForResize;
				}

				ACTUtils.SetScrollPosition($(self.element), scrollInfo);
			}
		}

		self.resizeFunction = function(height,width) {
			self.width = width;
			self.height = height;
			if (self.rootProperty && self.rootProperty().resize) {
				self.rootProperty().resize(height, width);

				// the padding isn't take in consideration because of the span before the caption
				// hide and show it seems to make it work
				var $collapseButton = self.$element.find(".collapseButton");
				$collapseButton.css("display", "none");
				setTimeout(function() {
					$collapseButton.css("display", "");  // not block to avoid override the (custom)css
				}, 0);
			} else {
				self.askForResize = {width: width, height: height};
			}
		}

		params.baseModel.resizeFunctions[params.baseModel.resizeFunctions.length] = self.resizeFunction;

		params.baseModel.showFunctions.push(function() {
			if (self.rootProperty && self.rootProperty().onShow)
				self.rootProperty().onShow();
		});

		self.base.initialize();
	}

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

});

