
var zoomAutoCompleteBlackBoard = {
	notify:function (data, fragmentName) {
		if (fragmentName == zoomAutoCompleteBlackBoard.fragmentName) {
			zoomAutoCompleteBlackBoard.callback(data);
		}
	},
	
	observe:function (fragmentName, callback, resourceUrl) {
		zoomAutoCompleteBlackBoard.callback = callback;
		zoomAutoCompleteBlackBoard.fragmentName = fragmentName;
		$.getScript(resourceUrl)
	}

}

function zoomAutoComplete(field, basePath) {
	
	var INDEX_TREE_CUT_DEPTH = 5;
	
	function getIndexResourceURL(fragmentName){
		var url = basePath;
		for (var i = 0; i < fragmentName.length; i++) {
			var charAtI = fragmentName.charAt(i);
			url += "/" + (charAtI == " " ? "_" : charAtI);
		}
		url += ".js";
		return url;
	}

	function getIndexResource(fragmentName, callback) {
		zoomAutoCompleteBlackBoard.observe(fragmentName, callback, getIndexResourceURL(fragmentName));
	}

	function getTermPositionInIndex(term, idx, begin, end) {
		if (begin == end) {
			return null;
		}
		var pos = begin + Math.floor( (end - begin) / 2 );
		var key = idx[pos];
		if (term == key) {
			return pos;
		}
		if (term > key && (idx[pos+1] == null || term < idx[pos+1])) {
			return pos;
		}
		if (term < key) {
			return getTermPositionInIndex(term, idx, begin, pos)
		}
		if (term > key) {
			return getTermPositionInIndex(term, idx, pos, end)
		}
	}
	
	function getGetCompletionsInIndex(term, idx, begin, end) {
		if (begin == end) {
			return null;
		}
		var pos = begin + Math.floor( (end - begin) / 2 );
		var key = idx[pos].key;
		if (term == key) {
			return idx[pos].completions;
		}
		if (term < key) {
			return getGetCompletionsInIndex(term, idx, begin, pos);
		}
		if (term > key) {
			return getGetCompletionsInIndex(term, idx, pos+1, end);
		}
	}

	field.autocomplete(null, {
		matchSubset: false,
		delay: 400,
		selectFirst: false,
		googleLikeSelection: true,
		normalize: function(term, options) {
			return zoom.util.flattenStringAndClean(term);
		},
		isValid: function() {
			return trim(field.val()) != '';
		},
		request: function(term, callbackSuccess){
			// se o comprimento é superior ao do nó de corte
			if (term.length > INDEX_TREE_CUT_DEPTH) {
				// recupera o nó de corte com o índice
				getIndexResource(term.substr(0,INDEX_TREE_CUT_DEPTH), function(data) {
					// descobre a posição do bloco de índice em que se encontra o termo
					var pos = null;
					if (data != null) {
						pos = getTermPositionInIndex(term, data.idx, 0, data.idx.length);
					}
					if (pos != null) {
						// recupera o bloco do termo
						getIndexResource(term.substr(0,INDEX_TREE_CUT_DEPTH) + pos, function(data) {
							// procura pelas completions para o termo dentro do bloco
							var completions = null;
							if (data != null) {
								completions = getGetCompletionsInIndex(term, data.idx, 0, data.idx.length);
							}
							if (completions != null) {
								// invoca a callback com as encontradas
								callbackSuccess(completions.join("\n"));
							} else {
								callbackSuccess("");
							}
						});
					} else {
						callbackSuccess("");
					}
				});
			} else {
				// caso seja inferior ao nó de corte, busca nó
				getIndexResource(term, function(data) {
					// caso o nó exista
					if (data != null) {
						// retorna as completions daquele nó
						callbackSuccess(data.completions.join("\n"));
					} else {
						callbackSuccess("");
					}
				});
			}
		}
	})
}

