MIXAD.widget.AutoComplete = function (pageURI, searchParamName, searchInputId, searchResultId, searchResulCallBack)
{
	this._searchParamName = searchParamName;
	
	this._inputField = document.getElementById(searchInputId)

	// creation d'une iframe bidon pour voir apparaitre la liste de resultats par dessus tous les autres elements du formulaire
	// necessaire uniquement pour ie (qui sux)
	this._resultIframe = null;
	if (window.ActiveXObject)
	{
		this._resultIframe = MIXAD.common.createSimpleElement("iframe", "iframe-" + searchResultId, "", document.body);
		this._resultIframe.src = "about:blank";
		this._resultIframe.style.position = "absolute";
		this._resultIframe.style.zIndex = "0";
		this._resultIframe.style.display = "none";
	}

	// creation du div servant a afficher la liste de resultats
	this._resultDiv = MIXAD.common.createSimpleElement("div", searchResultId, "", document.body);

	// procedure de callback appelee a la validation d'un element de la liste de resultats
	this._searchResulCallBack = searchResulCallBack;

	// valeur par defaut, peut etre modifiee par la methode setContentType
	this._contentType = "text/html;charset=iso-8859-1";

	// valeur par defaut, peut etre modifiee par la methode setKeypressTimeout
	this._keypressTimeout = 800;
	this._keyPressDelay = null;

	// derniere recherche effectuee
	this._lastSearch = "";

	// nombre de resultats
	this._resultCount = -1;
	// index courant de la liste de resultats
	this._resultIndex = -1;
	// valeur en cours de la liste de resultats
	this._resultValue = "";
	// donnees supplementaires attachees a l'element selectionne de liste de resultats
	this._resultData = null;

	// hauteur de ligne pour un element de la liste de resultats
	this._resultLineHeight = 13;
	// nom de la classe css gérant l'affichage d'un element selectionné de la liste de resultats
	this._highLightClassName = "highLight";
	
	// on affiche le header par defaut
	this._headerVisibility = true;

	// valeur de timeout au dela de laquelle la requete de recherche est consideree
	// comme un echec
	this._processTimeout = 3;	
	this._processTimerCount = 0;
	this._timedOut = 0;

	this._columnList = new Array();

	// la liste de resultats est active
	this._isActive = false;
	// un element de la liste de resultats est en cours de selection
	this._isSelecting = false;
	

	// nom de l'element xml pour lequel il faut recuperer des resultats (i.e : commune pour <commune>...</commune>)
	this._elementName = null;

	// tableau pour stocker d'eventuels parametre supplementaires
	this._additionalParam = new Array();

	// tableau pour stocker toutes les donnees attachees a un element de la liste de resultats
	this._itemData = null;


	// on initialise l'objet xmlHttp une bonne fois pour toute
	//this._httpObject = MIXAD.common.getXmlHttp();


	// on desactive l'autocomplete sur le champ de recherche
	this._inputField.setAttribute('autocomplete', 'off');

	// gestionnaire d'evenement pour declencher la recherche
	MIXAD.common.addEvent(this._inputField, 'keyup', this._startSearch, this);

	// gestionnaire d'evenement pour gerer la validation de recherche (touche entree appuyee)
	MIXAD.common.addEvent(this._inputField, 'keypress', this._keyPress, this);

	// gestionnaire d'evenement pour gerer l'appui sur les touches dans le champ d'edition
	MIXAD.common.addEvent(this._inputField, 'keydown', this._keyDown, this);

	// gestionnaire d'evenement pour gerer la sortie du champ d'edition
	MIXAD.common.addEvent(this._inputField, 'blur', this._blur, this);


	// proprietes de la classe mere
	this._pageURI = pageURI;
	this._requestProcessCallBack = this._processSearchResult;

	this._init();
}

// on herite de la classe XMLHttpRequest
MIXAD.widget.AutoComplete.prototype = new MIXAD.tool.XMLHttpRequest;


/**************************************************************************************
***	METHODES PUBLIQUES
***************************************************************************************/
MIXAD.widget.AutoComplete.prototype.addNewColumn = function (fieldName, headerCaption, headerClassName, itemClassName, defaultValue)
{
	var newIndex = this._columnList.length;
	this._columnList[newIndex] = new columnInfo(fieldName, headerCaption, headerClassName, itemClassName, defaultValue)
}
MIXAD.widget.AutoComplete.prototype.getResultValue = function ()
{
	return this._resultValue;
}
MIXAD.widget.AutoComplete.prototype.getResultData = function ()
{
	return this._resultData;
}
MIXAD.widget.AutoComplete.prototype.resetLastSearch = function ()
{
	this._lastSearch = "";
}

MIXAD.widget.AutoComplete.prototype.setElementName = function (elementName)
{
	this._elementName = elementName;
}
MIXAD.widget.AutoComplete.prototype.setAdditionalParam = function (paramName, paramValue)
{
	this._additionalParam[paramName] = paramValue;
}
MIXAD.widget.AutoComplete.prototype.setKeypressTimeout = function (keypressTimeout)
{
	this._keypressTimeout = keypressTimeout;
}
MIXAD.widget.AutoComplete.prototype.setProcessTimeout = function (processTimeout)
{
	this._processTimeout = processTimeout;
}
MIXAD.widget.AutoComplete.prototype.setResultLineHeight = function (resultLineHeight)
{
	this._resultLineHeight = resultLineHeight;
}
MIXAD.widget.AutoComplete.prototype.setHighLightClassName = function (highLightClassName)
{
	this._highLightClassName = highLightClassName;
}
MIXAD.widget.AutoComplete.prototype.setHeaderVisibility = function (headerVisibility)
{
	this._headerVisibility = headerVisibility;
}

/**************************************************************************************
***	METHODES PRIVEES
***************************************************************************************/
MIXAD.widget.AutoComplete.prototype._startSearch = function (e, cInstance)
{
	if (e.keyCode != 38 && e.keyCode != 40)
	{
		if (cInstance._keyPressDelay)
		{
			window.clearTimeout(cInstance._keyPressDelay);
		}

		if (cInstance._inputField.value != "")
		{
			// on attend keypressTimeout ms avant de lancer la recherche
			cInstance._keyPressDelay = window.setTimeout(function() { cInstance._launchSearch(); }, cInstance._keypressTimeout);
		}
		else
		{
			cInstance._resetEverything();
		}
	}
}

MIXAD.widget.AutoComplete.prototype._keyPress = function (e, cInstance)
{
	// touche entree, on valide la selection
	if (e.keyCode == 13)
	{
		MIXAD.common.stopPropagation(e);
		cInstance._validSearch();

		return false;
	}
}


MIXAD.widget.AutoComplete.prototype._keyDown = function (e, cInstance)
{
	// touche echappe, on annule tout
	if (e.keyCode == 27)
	{
		MIXAD.common.stopPropagation(e);
		
		cInstance._resetEverything();

		return false;
	}
	if (cInstance._resultVisible())
	{
		// fleche vers le haut
		if (e.keyCode == 38)
		{
			MIXAD.common.stopPropagation(e);
			
			if (cInstance._resultIndex > 0)
			{
				cInstance._resultIndex--;

				var index = cInstance._resultIndex;
				MIXAD.common.fireMouseEvent("searchListItemCol0_" + (index+1), "mouseout");
				MIXAD.common.fireMouseEvent("searchListItemCol0_" + index, "mouseover");
			}		

			return false;
		}
		// fleche vers le bas
		if (e.keyCode == 40)
		{
			MIXAD.common.stopPropagation(e);

			if (cInstance._resultIndex < cInstance._resultCount-1)
			{
				cInstance._resultIndex++;

				var index = cInstance._resultIndex;
				MIXAD.common.fireMouseEvent("searchListItemCol0_" + (index-1), "mouseout");
				MIXAD.common.fireMouseEvent("searchListItemCol0_" + index, "mouseover");
			}

			return false;
		}
	}
}

MIXAD.widget.AutoComplete.prototype._blur = function (e, cInstance)
{
	if (cInstance._isActive && !cInstance._isSelecting)
	{
		cInstance._lastSearch = "";
		cInstance._resetEverything();
	}
}


MIXAD.widget.AutoComplete.prototype._launchSearch = function ()
{
	var cInstance = this;

	if (cInstance._elementName)
	{
		if (cInstance._lastSearch != cInstance._inputField.value)
		{
			cInstance._processTimerCount = 0;
			cInstance._processTimer();

			// uniquement necessaire pour ie (qui SUX)
			this._httpObject.abort();

			// parametre principal de recherche
			cInstance.setSearchParam(cInstance._searchParamName, escape(cInstance._inputField.value));
			// parametres supplementaires
			for (var paramName in cInstance._additionalParam)
			{
				cInstance.setSearchParam(paramName, cInstance._additionalParam[paramName]);
			}

			// on lance la recuperation des donnees du serveur
			cInstance._getDataFromServer();

			cInstance._lastSearch = escape(cInstance._inputField.value);
		}
	}
	else
	{
		alert("Veuillez renseigner l'\"elementName\"");
	}
}



MIXAD.widget.AutoComplete.prototype._processSearchResult = function ()
{
	if (this._httpObject.readyState == 4)
	{
		this._resultDiv.innerHTML = "";

		var headerHeight = 0;

		// creation de l'entete dans le cas ou headerVisibility = true
		if (this._headerVisibility)
		{
			for (c = 0; c < this._columnList.length; c++)
			{
				var headerCaption = this._columnList[c].headerCaption;
				var headerClassName = this._columnList[c].headerClassName;

				var divObj = MIXAD.common.createSimpleElement("div", "searchListHeader" + c, headerClassName, this._resultDiv);
				var spanObj = MIXAD.common.createSimpleElement("span", "searchListHeader" + c, headerClassName, divObj);
				var aTextObj = document.createTextNode(headerCaption);
				
				spanObj.appendChild(aTextObj);
			}
			// recuperation de la hauteur de l'entete
			headerHeight = parseInt(MIXAD.common.getCssValue("searchListHeader0", "height"));
		}
		

		var xmlDoc = MIXAD.common.getXmlDocumentFromRequest(this._httpObject);
		var xmlData = xmlDoc.getElementsByTagName(this._elementName);


		// initialisation du tableau contenant les donnees attachees a un element de la liste de resultats
		this._itemData = new Array(xmlData.length);
		
		for (var i = 0; i < xmlData.length; i++)
		{		
			// donnees associees a l'element de la liste de resultats
			var childList = xmlData[i].childNodes;

			// tableau de stockage des donnees
			this._itemData[i] = new Array(childList.length);

			// recuperation des donnees
			for (var j = 0; j < childList.length; j++)
			{
				var childName = childList[j].nodeName;
				if (childName != "#text")
				{
					if (xmlData[i].getElementsByTagName(childName)[0].firstChild)
					{
						var childValue = xmlData[i].getElementsByTagName(childName)[0].firstChild.nodeValue;
						this._itemData[i][childName] = childValue;
					}
				}
			}

			// creation de la liste de resultats
			var ulObj = MIXAD.common.createSimpleElement("ul", "searchList", "", this._resultDiv);
			var liObj = MIXAD.common.createSimpleElement("li", "searchListItem_" + i, "resultItem");						// TODO: inclure parametrage du nom de la classe
			var cntObj = MIXAD.common.createSimpleElement("div", "searchListItemContainer_" + i, "resultContainer");		// TODO: inclure parametrage du nom de la classe


			// creation des colonnes pour l'element en cours
			for (c = 0; c < this._columnList.length; c++)
			{	
				// on colle un espace par defaut
				var colData = "\u00a0";

				// si une valeur par defaut est renseignee on en tient compte
				if (this._columnList[c].defaultValue)
				{
					colData = this._columnList[c].defaultValue;
				}

				if (this._itemData[i][this._columnList[c].fieldName])
				{
					colData = this._itemData[i][this._columnList[c].fieldName];
				}
				var colObj = MIXAD.common.createSimpleElement("div", "searchListItemCol" + c + "_" + i, this._columnList[c].itemClassName);			
				var aTextObj = document.createTextNode(colData);
				colObj.appendChild(aTextObj);

				// affectation de gestionnaires d'evenements au nouvel item de la colonne
				MIXAD.common.addEvent(colObj, 'mouseover', this._itemMouseOver, this);
				MIXAD.common.addEvent(colObj, 'mouseout', this._itemMouseOut, this)
				MIXAD.common.addEvent(colObj, 'click', this._itemClick, this);

				// ajout de la nouvelle colonne au container
				cntObj.appendChild(colObj);
				
				// on rend actif le premier element de la liste de resultats
				if (i == 0)
				{
					if (c == 0) { this._resultValue = colData; }

					colObj.className += " highLight";
				}
			}

			liObj.appendChild(cntObj);
			ulObj.appendChild(liObj);
		}

		this._resultCount = xmlData.length;
		this._resultIndex = 0;
	

		// recuperation des coordonnees de la popup de resultats
		var inputFieldCoord = MIXAD.common.getAbsoluteCoordinates(this._inputField);

		// positionnement du div de la liste de resultats
		this._resultDiv.style.left = inputFieldCoord.left + "px";
		this._resultDiv.style.top = inputFieldCoord.top + inputFieldCoord.height + "px";

		// dimensionnement du div de la liste de resultats
		this._resultDiv.style.height = (headerHeight + (this._resultCount*this._resultLineHeight)) + "px";

		// petit hack pour voir apparaitre la liste de resultats par dessus tous les autres elements du formulaire
		// necessaire uniquement pour ie (qui sux)
		if (this._resultIframe)
		{
			this._resultIframe.style.left = this._resultDiv.style.left;
			this._resultIframe.style.top = this._resultDiv.style.top;
			this._resultIframe.style.height = this._resultDiv.style.height;
			this._resultIframe.style.display = "block";
		}

		// affichage de la liste de resultats
		this._resultDiv.style.display = "block";

		this._isActive = true;
		this._processTimerCount = -1;
		this._processTimer();
	}
}

MIXAD.widget.AutoComplete.prototype._validSearch = function ()
{
	this._resultData = this._itemData[this._resultIndex];

	this._lastSearch = this._resultValue;
	this._inputField.value = this._resultValue;

	// on colle le curseur a la fin du champ d'edition
	var cursorPos = cInstance._inputField.value.length;
	MIXAD.common.setCursorPosition(cInstance._inputField, cursorPos, cursorPos);

	this._inputField.focus();

	this._searchResulCallBack.call();

	this._resetEverything();
}

MIXAD.widget.AutoComplete.prototype._itemMouseOver = function (e, cInstance)
{
	var newIndex = this.id.split("_")[1];
	var selectedItem = (e.srcElement) ? e.srcElement : e.target;
	var selectedIndex = selectedItem.id.split("_")[1];

	var index = newIndex;

	// une element a deja été selectionné via les touches du clavier, un nouveau est choisi via la souris
	if (cInstance._resultIndex != selectedIndex)
	{
		// on desactive la / les colonnes de l'element precedemment selectionné
		for (var c = 0; c < cInstance._columnList.length; c++)
		{
			var col = document.getElementById("searchListItemCol" + c + "_" + cInstance._resultIndex);
			col.className = col.className.replace(" highLight", "");
		}
		index = selectedIndex;
	}

	// on rend active la / les colonnes de l'element selectionné
	for (var c = 0; c < cInstance._columnList.length; c++)
	{
		var col = document.getElementById("searchListItemCol" + c + "_" + index);
		col.className += " highLight";
	}

	cInstance._resultIndex = newIndex;
	cInstance._resultValue = cInstance._itemData[index][cInstance._columnList[0].fieldName];
	cInstance._isSelecting = true;
}

MIXAD.widget.AutoComplete.prototype._itemMouseOut = function (e, cInstance)
{
	var index = this.id.split("_")[1];

	for (var c = 0; c < cInstance._columnList.length; c++)
	{
		var col = document.getElementById("searchListItemCol" + c + "_" + index);
		col.className = col.className.replace(" highLight", "");
	}
	cInstance._isSelecting = false;
}

MIXAD.widget.AutoComplete.prototype._itemClick = function (e, cInstance)
{
	cInstance._validSearch();
}

MIXAD.widget.AutoComplete.prototype._resultVisible = function ()
{
	return this._resultDiv.style.display == "block";
}

MIXAD.widget.AutoComplete.prototype._processTimer = function ()
{
	cInstance = this;
	if (cInstance._processTimerCount != -1)
	{
		// on se situe encore en dessous des processTimeout secondes de traitement
		if (cInstance._processTimerCount < cInstance._processTimeout)
		{
			cInstance._processTimerCount++;
			window.setTimeout(function() { cInstance._processTimer(); }, 1000);
		}
		// timed out
		else
		{
			cInstance._processTimerCount = 0;
			cInstance._httpObject.abort();

			return false;
		}
	}
	else
	{
		cInstance._processTimerCount = -1;
	}
	return true;
}


MIXAD.widget.AutoComplete.prototype._resetEverything = function ()
{
	if (this._resultIframe)
	{
		this._resultIframe.style.display = "none";
	}
	this._resultDiv.style.display = "none";
	this._processTimerCount = -1;
	this._resultCount = -1;
	this._resultIndex = -1;
	this._isActive = false;
	this._isSelecting = false;

	if (MIXAD.common.isSet(this._httpObject))
	{
		this._httpObject.abort();
	}
}

function columnInfo(fieldName, headerCaption, headerClassName, itemClassName, defaultValue)
{
	this.headerCaption = headerCaption;
	this.fieldName = fieldName;
	this.headerClassName = headerClassName;
	this.itemClassName = itemClassName;
	if (defaultValue)
	{
		this.defaultValue = defaultValue;
	}
}
