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

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

		self.utils = new PropertyUtils();
		self.utils.Init(self, params, element);

		var $element = $(element);
		params.$arrow = $element.find(".fancyArrow");
		params.$input = $element.find("input");
		params.$options = $element.find(".propertyOptions");

		if (!params.Enable)
		{
			params.$input.attr('readonly', true);
			params.$input.addClass("noselect");
		}

		params.updateStyle = function() {
			// positioning the fancy arrow
			var height = params.$input.outerHeight(true);

			var arrowHeight = params.$arrow.outerHeight(true);
			params.$arrow.css("top", (height - arrowHeight) * 0.5 + "px");
		}

		params.popUpIsOpen = function() {
			return params.$popUp != null;
		}

		params.hidePopUp = function() {
			if (!params.popUpIsOpen())
				return;
			params.$popUp.remove();
			params.$popUp = null;
		}

		params.selectItem = function($option, scrollToValue) {
			// deselect the previous one
			var $options = params.$popUp.find(".propertyOption");
			var $selectedItem = $options.filter(".selected");
			if ($selectedItem.length != 0)
				$selectedItem.removeClass("selected");

			// select the new one
			if ($option && $option.length > 0)
			{
				$option.addClass("selected");

				// scroll top the value if not visible
				var optionPosTop = $option.position().top;
				var optionPosBottom = optionPosTop + $option.height();
				var viewTop = 0 // not params.$popUp.scrollTop(); cause optionPosTop is already relative to that
				var viewHeight = params.$popUp.height()
				var viewBottom = viewTop + viewHeight;

				if (scrollToValue && (optionPosTop < viewTop || optionPosBottom > viewBottom)) {
					params.$popUp.scrollTop(params.$popUp.scrollTop() + optionPosTop - (viewHeight*0.5));
				}
			}
		}

		params.showPopUp = function() {
			if (params.optionValues().length == 0 || params.popUpIsOpen())
				return;

			// in case of popups that are still open but not our (can happen when call Refresh with the popup open)
			$(".propertyOptionsPopUp").remove();

			// timeout in case the data-bind doesn't pass yet (init)
			params.directionPressed = false;
			params.$popUp = params.$options.clone();
			$("body").append(params.$popUp);
			params.$popUp.addClass("propertyOptionsPopUp");

			var inputOffset = params.$input.offset();
			params.$popUp.offset({
				left: inputOffset.left,
				top :inputOffset.top + params.$input.outerHeight(true)
			});

			// same as input width
			params.$popUp.outerWidth(params.$input.outerWidth());

			// to be on front of next properties
			params.$popUp.css("z-index", parseInt(params.$input.zIndex()) + 1);

			params.$popUp.show();
			var $popUpOptions = params.$popUp.find(".propertyOption");

			for (var i = 0; i < params.optionValues().length; i++) {
				// select the current value
				var $option = $($popUpOptions[i]);
				if ($option.html() == params.value())
				{
					params.selectItem($option, true);
				}

				// add tooltip if necessary
				$option.css("position", "absolute");
				if ($option.outerWidth() > params.$popUp.width()) {
					ACTUtils.CreateTooltip($option, $option.html());
				}
				$option.css("position", "");
			}

			// scrollbar click that can call blur
			params.$popUp.on("mousedown", function(e) {
				e.preventDefault();
			});

			$popUpOptions.on("mousedown", function(e) {
				e.preventDefault();  // to not call blur before calling click : http://stackoverflow.com/questions/10652852/jquery-fire-click-before-blur-event
			}).on("click", function(e) {
				params.onOptionValidate();

				/*
				 * cause blur will call the value change cb and we don't want to
				 * validate the value the user type but the value the user click
				 */
				params.updating = true;
				params.$input.blur();
				params.updating = false;
			});

			$popUpOptions.mouseenter(params.onOptionMouseEnter);
		}

		params.$input.on("mousedown", function(e) {
			if (params.popUpIsOpen() && !params.Enable) {
				params.$input.blur(); // if we only close we couldn't refocus/reopen the popup
				params.noFocus = true;
			}
		});

		$(window).on("mouseup", function(e) {
			params.noFocus = false;
		});

		params.$input.on("focus", function(e) {
			if (params.noFocus) {
				params.$input.blur(); // we need to reblur cause we focus it again
				return;
			}

			if (!params.popUpIsOpen()) {
				params.showPopUp();
			} else {
				params.hidePopUp();
			}
		});

		params.$input.on("blur", function(e) {
			params.hidePopUp();
		});

		params.$input.on("keydown", function(e) {
			if (params.popUpIsOpen()) {
				var retValue = true;
				var code = e.keyCode;
				var keyUpCode = 38;
				var keyDownCode = 40;
				var keyEnterCode = 13;

				var $options = params.$popUp.find(".propertyOption");
				var $selectedItem = $options.filter(".selected");
				var $itemToSelect = null;

				if (code == keyUpCode || code == keyDownCode) {
					params.directionPressed = true;

					if ($selectedItem.length == 0) {
						if (code == keyUpCode) {
							$itemToSelect = $options.last();
						} else {
							$itemToSelect = $options.first();
						}
					} else {
						var selectedIndex = $selectedItem.index();
						if (code == keyUpCode) selectedIndex--;
						else                   selectedIndex++;

						var optionsCount = $options.length;
						if (selectedIndex < 0) selectedIndex = optionsCount - 1;
						if (selectedIndex >= optionsCount) selectedIndex = 0;

						$itemToSelect = $($options[selectedIndex]);
						retValue = false;
					}
				} else if (code != keyEnterCode) {  // the user type something
					params.directionPressed = false;
					return true;
				} else {
					// enter to validate the options
					params.onOptionValidate();

					// if we don't blur after handling the input, the input stay focus
					setTimeout(function() { params.$input.blur(); }, 0);
					return true;
				}

				params.selectItem($itemToSelect, true);
				return retValue;
			}
		});

		params.onResize = function() {
			if (params.popUpIsOpen()) {
				params.hidePopUp();
				params.showPopUp();
			}
		};

		params.beforeChange = function(newVal) {
			params.hidePopUp();
		}

		params.onOptionMouseEnter = function(e) {
			if (!params.popUpIsOpen())
				return;

			params.selectItem($(e.target), false);
		}

		params.onOptionValidate = function() {
			var $target = params.$popUp.find(".selected");

			if ($target.text() == this.value()) {
				return;
			}

			var parentKoComp = ko.contextFor(element).$parent;
			parentKoComp.parent.params.baseModel.onTriggerAction("TriggerValueChange", [params.Name, $target.text()]);

			// if this is a component we don't want to notify the change with the value but with the selected index
			params.onchange($target.index(), true);  // true to say it's an index
		};

		self.utils.Ready(params);
		return params;
    }

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