

/**
 * Définit le code métier lié au système de proposition. A mettre en relation avec le fichier templateProposition qui définit le code lié à l'affichage.
 * cf page de test esv/tests/PropositionMEV.html
 *
 */
(function() {
	//'use strict';
	// Si les namespaces/classes nécessaires ne sont pas chargées : exception
	var WIN = this,
			EASY = WIN.ev,
			evalIsEvil = eval;



	if (!EASY) {throw new Error("Le namespace 'EASY' doit exister");}
	if (!EASY.rjs) {throw new Error("Le namespace 'EASY.rjs' doit exister");}
	// On s'assure que le namespace EASY.me existe
	if (!EASY.me) { EASY.me = {}; }
	//Si la classe Compte est définie on sort
	if (EASY.me.PropositionManager) {return;}

	EASY.me.PropositionManager = [];


	/**
	 * Permet d'extraire le nom de lieu depuis une chaine de code correctement structurée...
	 * Format du code ville de Londres : v:4173|c:LON|t:Londres,
	 * @param {Object} dataMEV code complet correctement formé correspondant au lieu.
	 */
	function extractNomLieuFromReponse(dataMEV) {
		var refString = '|t:';
		var indexNomLieu = dataMEV.indexOf(refString);
		var nomLieu = dataMEV.substring(indexNomLieu + refString.length, dataMEV.length);
		return nomLieu;
	}




	/**
	 *  Utilisé pour stocker des informations sur le panneau de proposition ouvert.
	 *  D'un point de vue fonctionnel, l'on considère qu'nn seul panel de proposition ( fenetre contenant la liste des propositions ) doit être
	 *  ouvert à un instant donné.
	 *  Si un panel associé à un champ est ouvert et que l'internaute désire afficher les propositions associées à un autre champ, le panneau
	 *  ouvert est fermé, puis le dernier demandé est ouvert.
	 */
	var openedPanel;
	/**
	 * Longueur maximale de la chaine de caractère contenant le lieu saisi par l'internaute.
	 * Au dessus de cette limite, la requête de validation du lieu saisi ne sera pas réalisé.
	 */
	var MAX_LIEU_NAME_LENGTH = 25;
	/**
	 * Longueur minimale de la chaine de caractère contenant le lieu saisi par l'internaute.
	 * En dessous de cette limite, la requête de validation du lieu saisi ne sera pas réalisé.
	 */
	var MIN_LIEU_NAME_LENGTH = 3;


	/**
		 * Fonction de toggle sur le panel de proposition. Si ouvert on le ferme, si fermé on l'ouvre.
		 * Lorsque l'on ferme le panel on supprime la node du DOM, et on la génère sur le open.
		 * @param {Object} _propositions Liste des propositions.
		 * @param {Object} _fieldIdStr id du champs texte.
		 * @param {Object} onPropositionSelected action à dérouler lorsqu'une proposition est sélectionnée.
		 */
	function TogglePropPanel(_propositions,_fieldIdStr,onPropositionSelected) {
		/** false panneau caché, état par défaut, true panneau affiché.*/
		var status = false;
		var onclickFunction = this.onclick;
		var This = this;
		this.onclick = function() {
			if (!status) {//caché
				status = true;
				if (!openedPanel) { // Premier panel ouvert on le stock
					openedPanel = {id: _fieldIdStr, call: onclickFunction, This: This, propositions: _propositions, fieldIdStr: _fieldIdStr, onPropositionSelected: onPropositionSelected};
				}
				else if (openedPanel.id !== _fieldIdStr) { // Panel ouvert n'est pas celui ci on ferme le premier et on stock celui ci
					openedPanel.This.onclick();
					openedPanel = {id: _fieldIdStr, call: onclickFunction, This: This, propositions: _propositions, fieldIdStr: _fieldIdStr, onPropositionSelected: onPropositionSelected};
				}

				EASY.tpl.templatePropositions.display(_propositions, _fieldIdStr, onPropositionSelected, this.onclick);
			}else {
				status = false;
				openedPanel = null;
				EASY.tpl.templatePropositions.close(_fieldIdStr);
			}
		};
	}



	/**
	 * Permet de gérer la validtié des champs utilisant le système de proposition au sein d'un formulaire.
	 * @param {Object} _fields tableau d'objet EASY.me.PropositionManager.ValidationRequest.
	 */
	EASY.me.PropositionManager.RequestManager = function(_formId,_submitFormId) {
		/**
		 * Tableau associatif, associé un id de champs à un objet ValidationRequest.
		 */
		var requests = [];
		/**
		 * Méthode à appelé si tous les champs sont valides.
		 */
		var onAfter;
		/**
		 * setter propriété onAfter
		 * @param {function} _onAfter méthode à appelée.
		 */
		this.setOnAfter = function(_onAfter) {
			onAfter = _onAfter;
		};
		/**
		 * Permet d'ajouter correctement un élément à la liste requests
		 * @param {EASY.me.PropositionManager.ValidationRequest} _field Validateurs à ajouter à la liste.
		 */
		this.addField = function(_vr) {
			requests.push(_vr);
		};
		/**
		 * Parcours la liste des validateurs, et renvoi true si ils sont tous valide.
		 * Ce qui rEASY.ent, à vérifier que tous les champs soumis au système de propositions ont un contenu valide.
		 * Champs texte + champs caché remplis.
		 */
		function isFormValid() {
			var i;
			for (i = 0; i < requests.length; i++) {
				if (!requests[i].isValid()) {
					return false;
				}
			}
			return true;
		}
		/**
		 * définit le traitement à executé lorqu'une validation abouti à un match direct.
		 */
		function fireMatching() {
			if (isFormValid() && onAfter) {
				if (onAfter) {
					onAfter(_formId);
				}
			}
		}
		/**
		 * définit le traitement à executé lorqu'une validation n'abouti pas à un match direct.
		 */
		function fireNoDirectMatch() {
			var i;
			if (!isFormValid()) {
				var btsubmit = EASY.dom.element(_submitFormId);
				if (btsubmit) {
					if (WIN.resetSubmitMEVButton) {
						WIN.resetSubmitMEVButton();
					}
				}
				for (i = 0; i < requests.length; i++) {
					requests[i].setMatchinCallBak(null);
					requests[i].setNoMatchCallBak(null);
				}
			}
		}
		/**
		 * Parcours la liste des champs enregistré et lance les requêtes de validation si non validée.
		 */
		function sendRequests() {
			var i;
			for (i = 0; i < requests.length; i++) {
				if (!requests[i].isValid()) {
					requests[i].setMatchinCallBak(fireMatching);
					requests[i].setNoMatchCallBak(fireNoDirectMatch);
					requests[i].callSend();
				}
			}
		}
		/**
		 * Méthode à appelé lors du controle de la validité du formulaire
		 */
		this.check = function() {
			sendRequests();
		};
	};


	/**
	 *Cet objet permet d'ajouter un controle du contenu d'un champs texte de formulaire format easyvoyage ( Champs texte associé à un champs data ).
	 *ex declaration => var propManager = new EASY.me.PropositionManager.ValidationRequest({'str':'lieuMEVDepartAller','code':'dataMEVDepartAller'});
	 *
	 * Utilise le service ERA /vols/geo/lieux/valider.rjs, avec le paramètre 'departAller'.
	 * @param {Object} _fieldId Objet contenant les id des champs textes. {'str':'lieuMEVDepartAller','code':'dataMEVDepartAller'};
	 *      str  = id HTML du champs texte contenant la saisie de l internaute.
	 *      code = id HTML du champs texte contenant le code du lieu, utilisé aussi par l'autocompletion...
	 * @param {Object} _onAfter Methode à appelée sur la réponse serveur. Se verra passer l'objet result entier en paramètre (Optionnel).
	 * FIXME commentaire obsolète : param {string} _moteur Chaine de caractère contenant le nom du moteur sur lequel on veut trouver des propositions.
	 */
	EASY.me.PropositionManager.ValidationRequest = function(_fieldId,_onAfter) {
		/** Pointer vers l'instance de ValidationManager. ( Permet d'éviter les confusions avec le mot cl) */
		var request = this;
		// Stockage du moteur à utiliser. Permet d'utiliser l'url du service geo adéquate en fonction du moteur.
		var moteur = 'MEV';
		if (_fieldId && _fieldId.moteur) {
			moteur = _fieldId.moteur;
		}
		/** On génère un id pour cet instance, composé de l'id du champs texte et d'une graine, pour le moment la chaine 'VR'*/
		this.id = _fieldId.str + 'VR';
		/**Méthode à appeler lors d'un matching direct dans les propositions.*/
		var matchinCallBak;
		/**Call back à appelé quand pas de match direct, liste de propositions ou pas de proposition.*/
		var noMatchCallBak;
		//
		var onReceiveCallback;
		/** Variable de status, 0 status en attente, 1 demande de validation en cours.*/
		var mStatus = 0;
		/** Contient la node du champs texte*/
		var fieldNode = EASY.dom.element(_fieldId.str);
		/** Contient la node du champs data associé au champs texte.**/
		var dataFieldNode = EASY.dom.element(_fieldId.code);
		/** Locator propre à cette instance de ValidationRequest, utilise l'id du champs à vérifier comme identificateur de requete ERA.*/
		this.mLocator = new EASY.rjs.Locator(10000, _fieldId.str);
		/**Sauvegarde de l'évènement onblur déjà présent sur le champ.*/
		var oldOnBlur = fieldNode.onblur;
		/** Compatibilité ancien système fonctionnat avec des EVAL.*/
		var oldOnBlurEVAL;
		if (!oldOnBlur) {
			oldOnBlurEVAL = fieldNode.getAttribute('onblur');
		}


		/**
		 * Setter. Cf déclaration de la propriété matchinCallBak.
		 */
		this.setMatchinCallBak = function(_function) {
			matchinCallBak = _function;
		};
		/**
		 * Setter. Cf déclaration de la propriété NoMatchCallBak.
		 */
		this.setNoMatchCallBak = function(_function) {
			noMatchCallBak = _function;
		};
		
		
		this.setOnReceiveCallback=function(_function) {
			if(_function&&typeof(_function)==='function') {
				onReceiveCallback=_function;
			}
		};
		/**
		 * Return true if dataFieldNode is not empty.
		 * FIXME Ajouter un controle du format du champs data serait peut être une bonne idée, à voire....
		 */
		this.isValid = function() {
			if (dataFieldNode && dataFieldNode.value !== '' && dataFieldNode.value !== 'undefined') {
				return true;
			}
			return false;
		};

		/**
		 * Evenement appelé lorsque l'internaute séléctionne une proposition.
		 */
		function onPropositionSelected(lieu) {
			EASY.dom.element(_fieldId.str).style.className = '';
			EASY.dom.element(_fieldId.str).value = lieu.nom;
			EASY.dom.element(_fieldId.code).value = lieu.data;
			EASY.dom.element(_fieldId.str).setAttribute('adata', lieu.data);
			if (lieu.data2) { // data2 défini si type aeroport, contient les informations de la ville associée.
				EASY.dom.element(_fieldId.str).setAttribute('vdata', lieu.data2);
			}
			EASY.tpl.templatePropositions.closeErrorMSG(fieldNode);
		}
		/**
		 * Evenement, appelé lors de l'arrivée de la réponse serveur.
		 * @param {Object} _result tableau contenant la réponse ERA.
		 */
		var onReceive = {
			onSuccess: function(_result) {
				mStatus = 0;
				/**On supprime l'image de loading de la requête.*/
				EASY.tpl.templatePropositions.closeStatusRequestImg(fieldNode);
				/** Exception dans la réponse.*/
				if (_result.exception) {
					/**Affichage du message d'erreur sans lien vers les propositions car pas de proposition.*/
					EASY.tpl.templatePropositions.displayErrorMSG(fieldNode, null, false);
					return;
				}
				

				/** Matching réussi, lieu trouvé. On set le champs texte avec le nom du lieu et le champs code avec le code du lieu.*/
				if (_result.dataMEVDepartAller || _result.dataMEHLieu) {
					EASY.dom.element(_fieldId.str).value = extractNomLieuFromReponse(_result.dataMEVDepartAller || _result.dataMEHLieu);
					EASY.dom.element(_fieldId.code).value = _result.dataMEVDepartAller || _result.dataMEHLieu;
					/** Mise à jour de adata et vdata du champs texte si dataMEVDepartAller2 est définie**/
					EASY.dom.element(_fieldId.str).setAttribute('adata', _result.dataMEVDepartAller || _result.dataMEHLieu);
					if (_result.dataMEVDepartAller2) { // Défini si le lieu est un aéroport.
						EASY.dom.element(_fieldId.str).setAttribute('vdata', _result.dataMEVDepartAller2);
					}
					EASY.tpl.templatePropositions.closeErrorMSG(fieldNode);
					/** Appel de la méthode de call back si définie.*/
					if (matchinCallBak) {
						matchinCallBak();
					}
					
					if(onReceiveCallback) {
						onReceiveCallback(request);
						onReceiveCallback=undefined;
					}
					
					return;
				}
				/**FIX ME voir si toujours nécessaire.*/
				EASY.dom.element(_fieldId.str).style.className = 'onError';
				/** test si il y a des propositions dans la réponse de l'ERA.**/
				if (_result.propositions && _result.propositions.length > 0 && _result.propositions[0].lieux && _result.propositions[0].lieux.length > 0) {
					EASY.tpl.templatePropositions.closeErrorMSG(fieldNode);
					var bt = new TogglePropPanel(_result.propositions[0], _fieldId.str, onPropositionSelected);
					/**Affichage du message d'erreur contenant le lien vers les propositions*/
					EASY.tpl.templatePropositions.displayErrorMSG(fieldNode, bt.onclick, true);
				} else {
					/**Affichage du message d'erreur sans lien vers les propositions car pas de proposition.*/
					EASY.tpl.templatePropositions.displayErrorMSG(fieldNode, null, false);
				}
				/** appel de la méthode noMatchCallBak si définie...**/
				if (noMatchCallBak && typeof(noMatchCallBak === 'function')) {
					noMatchCallBak();
				}
				/** Méthode optionnel permettant de suffixer le traitement avec des process défini dans le cadre appelant de ValidationRequest....**/
				if (_onAfter) {
					_onAfter(_result);
				}
				
				if(onReceiveCallback) {
					onReceiveCallback(request);
					onReceiveCallback=undefined;
				}
				
			},
			/**
			* Méthode appelée lorsque la requête est en erreur. cf .EASY.requestManager.invokeEra et gestion des timeouts.
			* @param {EASY.rjs.Event} e évènement lancé lorsque la requête part en erreur.
			*/
			onError: function(e) {
				//On supprime l'image de loading de la requête.
				EASY.tpl.templatePropositions.closeStatusRequestImg(fieldNode);
				//Affichage du message d'erreur sans lien vers les propositions car pas de proposition.
				EASY.tpl.templatePropositions.displayErrorMSG(fieldNode, null, false);
				EASY.log.warn('e='+e);
				return;
			}
		};



		/**
		 * Lance la requête de validation au server ERA, la methode 'onReceive' est appelée à reception de la réponse.
		 * Méthode destinée à être appelée depuis un évenement d'element HTML.
		 * Le this se réfert donc à l'élement HTML même.
		 * exemple :
		 *  var request = new EASY.me.PropositionManager.ValidationRequest('fieldId',_onAfter).
		 *  <input type='text' onblur='request.send;'/>
		 */
		function send() {
			if (oldOnBlur) {
				oldOnBlur();
			}else {
				evalIsEvil(oldOnBlurEVAL);
			}
			//ev.log.info('me.PropositionManager.ValidationRequest#send(): '+moteur+', '+_fieldId.str+', '+_fieldId.code);

			/** Lecture du champs texte du formulaire.*/
			var _lieuxStr = fieldNode.value;
			/**Lecture du champs data associé au champs texte.*/
			var _lieuxCode = dataFieldNode.value;
			/** Le champ est vide on ne fait rien et on efface les eventuels messages d'erreur.**/
			if (_lieuxStr.length === 0) {
				EASY.dom.element(_fieldId.str).style.className = '';
				EASY.tpl.templatePropositions.closeErrorMSG(fieldNode);
				return '';
			}
			/** Une requête est déjà en cours pour ce champs, on ne peut lancer une requête si la première n'est pas terminée.*/
			if (mStatus === 1) {
				return '';
			}
			/** Le champ code associé à ce champs text n'est pas vide, le lieu est déjà valide. On supprime l'image de status et on quitte.**/
			if (_lieuxCode.length > 0) {
				EASY.dom.element(_fieldId.str).style.className = '';
				EASY.tpl.templatePropositions.closeErrorMSG(fieldNode);
				if(onReceiveCallback) {
					onReceiveCallback(request);
					onReceiveCallback=undefined;
				}
				return '';
			}
			/***/
			if (typeof(_lieuxStr) !== 'string') {
				return false;
			}
			if (_lieuxStr.length > MAX_LIEU_NAME_LENGTH) {
				EASY.tpl.templatePropositions.displayErrorMSG(fieldNode, null, false);
				return false;
			}

			if (_lieuxStr.length < MIN_LIEU_NAME_LENGTH) {
				//EASY.tpl.templatePropositions.displayErrorMSG(fieldNode, null, false);
				return false;
			}
			/** Affichage du logo de status de la requête.*/
			EASY.tpl.templatePropositions.displayStatusRequestImg(fieldNode);
			/**Requête va être lancée on passe le status à 1.*/
			mStatus = 1;
			/** Appel à .EASY.requestManager.invokeEra pour lancer une requête de validation sur ERA.*/

			// Par défaut on utilise le service du moteur vol.
			var url = '/vols/geo/lieux/valider.rjs?departAller=' + _lieuxStr + '&id=' + _fieldId.str;
			if (moteur === 'MEH') {
				url = '/hotels/geo/lieux/valider.rjs?lieu=' + _lieuxStr + '&id=' + _fieldId.str;
			}
			EASY.requestManager.invokeEra(request.mLocator, url, onReceive, true);
			return true;
		}

		/** Getter sur la méthode interne send.*/
		this.callSend = function() {
			send();
		};

		/**On associe l'envoi de la requete de validation sur le onblur du champs texte.*/
		fieldNode.onblur = send;

	};
	
	
	
	
	/**
	 * Permet de gérer la validtié des champs utilisant le système de proposition au sein d'un formulaire.
	 * @param {Object} _fields tableau d'objet EASY.me.PropositionManager.ValidationRequest.
	 */
	EASY.me.PropositionManager.RequestManager2 = function() {
		/**
		 * Tableau associatif, associé un id de champs à un objet ValidationRequest.
		 */
		var requests = [];
		
		/**
		 *  Compteur utilisé lors d'une vérification de tous les champs associé au manager.
		 *  Dés qu'une requête de validation est terminée pour un champs, on incrémente ce compteur.
		 */
		var nbChecked=0;
		
		/**
		 * Méthode à appelé si tous les champs sont valides.
		 */
		var onAfter;
		/**
		 * setter propriété onAfter
		 * @param {function} _onAfter méthode à appelée.
		 */
		this.setOnAfter = function(_onAfter) {
			if(_onAfter&&typeof(_onAfter)==='function') {
				onAfter = _onAfter;	
			}
		};
		/**
		 * Permet d'ajouter correctement un élément à la liste requests
		 * @param {EASY.me.PropositionManager.ValidationRequest} _field Validateurs à ajouter à la liste.
		 */
		this.addField = function(_vr) {
			requests.push(_vr);
		};
		/**
		 * Parcours la liste des validateurs, et renvoi true si ils sont tous valide.
		 * Ce qui rEASY.ent, à vérifier que tous les champs soumis au système de propositions ont un contenu valide.
		 * Champs texte + champs caché remplis.
		 */
		function isFormValid() {
			var i;
			for (i = 0; i < requests.length; i++) {
				if (!requests[i].isValid()) {
					return false;
				}
			}
			return true;
		}
		/**
		 * définit le traitement à executé lorqu'une validation abouti à un match direct.
		 */
		function fireMatching() {
			EASY.log.error('fireMatching');
			nbChecked++;
			if (onAfter&&nbChecked===requests.length) {				
				onAfter(isFormValid());
			}
		}		
		/**
		 * Parcours la liste des champs enregistré et lance les requêtes de validation si non validée.
		 */
		function sendRequests() {
			var i;
			nbChecked=0;
			for (i = 0; i < requests.length; i++) {
				if (!requests[i].isValid()) {
					requests[i].setOnReceiveCallback(fireMatching);					
					requests[i].callSend();
				}else{
					nbChecked++;					
				}
			}
			// Tous les champs peuvent être deja valides
			if(nbChecked===requests.length) {
				onAfter(isFormValid());
			}
			
			
		}
		/**
		 * Méthode à appelé lors du controle de la validité du formulaire
		 */
		this.check = function() {
			sendRequests();
		};
	};
	



	EASY.log.debug('EASY.me/PropositionManager.js ok');
}());

