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

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

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

		self.targetList = null;
		self.searchAttribute = "";
		self.hintText = params.Model.HintText;

		var $hintSearch  = $(element).find(".hintSearch");
		var $inputSearch = $(element).find(".inputSearch");
		var $clearButton = $(element).find(".clearButton");

		self.base.onRefresh = function(obj) {
			$inputSearch.css("width", obj.WidthPC + "%");

			self.hintText = obj.HintText;
			UpdateHint();

			var targetCompName = obj.TargetListComponentName;
			if (!targetCompName)
				return;

			var targetComp = params.baseModel.definition().gridPanel.componentModels[targetCompName];
			if (targetComp.definition().ComponentName == "componentListComponent") {
				self.targetList = targetComp;
				var dataChangedCallbacks = self.targetList.GetCollection("dataChangedCallbacks");
				dataChangedCallbacks.push(self.hideListElementUsingInput);
			}

			// create help tooltip
			self.help(obj.Help);
			if (obj.Help && obj.Help != "") {
				self.helpActivated = true;
				ACTUtils.CreateTooltip($inputSearch, obj.Help, true, {name:"helpTooltip",position:"bottom"});
				$inputSearch.mouseleave(function () {
					$inputSearch.jqxTooltip('close');
				});
				$inputSearch.focus(function () {
					$inputSearch.jqxTooltip('close');
					$inputSearch.jqxTooltip({disabled: true});
				});
				$inputSearch.blur(function () {
					$inputSearch.jqxTooltip({disabled: false});
				});
			}

			self.searchAttribute = obj.SearchAttributeName;
		}.bind(self);

		$clearButton.hide();

		$clearButton.on("click", function() {
			$inputSearch.val("");
			self.hideListElementUsingInput();
			$clearButton.hide();
			$inputSearch.focus();
			UpdateHint();
		});

		var UpdateHint = function() {
			var value = "";
			if ($inputSearch.val() == "")
				value = self.hintText;

			$hintSearch[0].innerHTML = value;
		}
		UpdateHint();
		$inputSearch.on("input", UpdateHint);

		$inputSearch.keyup(function() {
			self.hideListElementUsingInput();

			if ($inputSearch.val() != "") {
				$clearButton.show();
			} else {
				$clearButton.hide();
			}
		});

		// return true if the sequence of letter in searchWord is in target in the same order
		// with limitted distance between letters
		function SearchOk(searchWord, target) {
			if (searchWord.length == 0)  return true;
			if (target.length == 0)      return false;

			var searchWordLength = searchWord.length;
			var distanceMaxBetweenLetters = 6;

			var firstFindIndex = -1;
			do {
				var startI = firstFindIndex + 1;
				firstFindIndex = -1;

				// we need to keep the last find index to know the distance with the current index
				var lastFindIndex = -1;
				var searchIndex = 0;
				for (var i = startI; i < target.length; i++) {
					if (target[i] == searchWord[searchIndex]) {
						searchIndex++;
						if (searchIndex >= searchWordLength) {
							return true;
						}

						lastFindIndex = i;
						if (firstFindIndex == -1) {
							firstFindIndex = lastFindIndex;
						}
					}
					else if (lastFindIndex != -1 && (i - lastFindIndex) >= distanceMaxBetweenLetters) {
						break;
					}
				}
			} while(firstFindIndex != -1)  // We start again in case we found partially the searchWord and the searchWord is entirely present elsewhere

			return false;
		}

		self.hideListElementUsingInput = function() {
			if (self.targetList == null)
				return;

			const searchSEP = " ";
			const targetSEP = "|";
			const objectSEP = ":";

			// if we have a list we try to limitate to one level not all children (in case of panel)
			var targetElements = null;
			var listComps = $(self.targetList.element).find(".listComponents");
			if (listComps.length > 0) {
				targetElements = listComps.children().children(".content")
			} else {
				targetElements = $(self.targetList.element).children().find(".content");
			}

			var query = $inputSearch.val().toLowerCase();
			if (query == "comment vont les stars ?") {
				$inputSearch.val("Bien !");
				return;
			}

			var tab = query.split(searchSEP);
			var visibilityChange = false;
			for (var targetElementIndex = 0; targetElementIndex < targetElements.length; targetElementIndex++) {
				var targetElement = targetElements[targetElementIndex];
				if ($(targetElement).attr("ignore-search") ||  $(targetElement).hasClass("__IGNORE_SEARCH__"))
					continue;

				var targetTexts = $(targetElement).attr(self.searchAttribute);
				if (!targetTexts || targetTexts == "") {
					targetTexts = targetElement.textContent;
				}
				targetTexts = targetTexts.toLowerCase().split(targetSEP);
				var targetElementIsVisible = $(targetElement).css("display") == "block";

				var ok;
				for (var i=0; i<tab.length; i++) {
					ok = false;
					var searchText = tab[i].trim();
					var searchTextsSpec = searchText.split(objectSEP);

					for (var j = 0; j < targetTexts.length; j++) {
						var targetText = targetTexts[j];
						var targetTextsSpec = targetText.split(objectSEP);
						var isSpec = searchTextsSpec.length > 1 && targetTextsSpec.length > 1;

						if ((isSpec && SearchOk(searchTextsSpec[0], targetTextsSpec[0]) && SearchOk(searchTextsSpec[1], targetTextsSpec[1]))
						|| (!isSpec && SearchOk(searchText, targetText)))
						{
							ok = true;
							break;
						}
					}
					if (!ok)
						break;
				}

				if (ok != targetElementIsVisible) {
					$(targetElement).css("display", ok && "block" || "none");
					visibilityChange = true;
				}
			}

			if (visibilityChange) {
				if (listComps.length > 0) 
					ko.dataFor(listComps[0]).forceResize = true;
				self.targetList.resize();
			}
		}

		$hintSearch.on("click", function() {
			$inputSearch.focus();
		});

		params.baseModel.resizeFunctions[params.baseModel.resizeFunctions.length] = function(height,width) {
			var paddingRight = 5;
			$clearButton.css(
				"left",
				$inputSearch.position().left + $inputSearch.outerWidth(true)
				- $clearButton.outerWidth(true) - paddingRight
			);

			var buttonHeight = $clearButton.outerHeight();
			var inputHeight  = $inputSearch.outerHeight();
			$clearButton.css("top", (inputHeight - buttonHeight) * 0.5);

			var inputWidth   = $inputSearch.outerWidth();
			if (self.helpActivated) {
				$inputSearch.jqxTooltip({
					width: inputWidth * 0.75 + 'px',
				});
			}

			$hintSearch.css("width", ""); // to have the real width
			var offsetLeft = 32;
			var hintWidth = Math.min($hintSearch.width() + 5, width - offsetLeft);

			$hintSearch.css({
				"left"  : ($inputSearch.position().left + offsetLeft) + "px",
				"top"   : ($inputSearch.position().top + 5)   + "px",
				"width" : hintWidth + "px",
			});
		}

		self.base.initialize();
		params.ready(true);
	}

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