/**
 * 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(){
	// Si les namespaces/classes nécessaires ne sont pas chargées : exception
	if(!window.ev){throw new Error("Le namespace 'ev' doit exister");}
	if(!ev.rjs){throw new Error("Le namespace 'ev.rjs' doit exister");}
	// On s'assure que le namespace ev.me existe
	if(!ev.me){ ev.me={}; }
	//Si la classe Compte est définie on sort
	if(ev.me.PropositionManager) {return;}	
	
	ev.me.PropositionManager = [];
	/**
	 *  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;		
	/**
	 * 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 ev.me.PropositionManager.ValidationRequest.
	 */
	ev.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 {ev.me.PropositionManager.ValidationRequest}_field Validateurs à ajouter à la liste. 
		 */
		this.addField = function(_vr) {
			var cle = _vr.id;
			requests.push(_vr);
		};
		/**
		 * Parcours la liste des validateurs, et renvoi true si ils sont tous valide.		 
		 * Ce qui revient, à vérifier que tous les champs soumis au système de propositions ont un contenu valide.
		 * Champs texte + champs caché remplis.
		 */
		function isFormValid() {
			for(var 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() {			
			if(!isFormValid()) {			
				var btsubmit=ev.dom.element(_submitFormId);					
				if(btsubmit) {
					btsubmit.className='';
				}						
				for(var 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() {
			for(var 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 ev.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).
	 */		
	ev.me.PropositionManager.ValidationRequest = function(_fieldId,_onAfter) {
		/** Pointer vers l'instance de ValidationManager. ( Permet d'éviter les confusions avec le mot cl) */
		var request = this;		
		/** 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;
		/** Variable de status, 0 status en attente, 1 demande de validation en cours.*/
		var mStatus = 0;
		/** Contient la node du champs texte*/					
		var fieldNode = ev.dom.element(_fieldId.str);
		/** Contient la node du champs data associé au champs texte.**/
		var dataFieldNode = ev.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 ev.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');
		}		
		/**On associe l'envoi de la requete de validation sur le onblur du champs texte.*/
		fieldNode.onblur = send;						
		
		/**
		 * 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;
		};
		/**
		 * 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) {					
			ev.dom.element(_fieldId.str).style.className ='';			
			ev.dom.element(_fieldId.str).value=lieu.nom;
			ev.dom.element(_fieldId.code).value=lieu.data;				
			ev.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.
				ev.dom.element(_fieldId.str).setAttribute('vdata',lieu.data2);	
			}						
			ev.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={		   	   		
           onSucces:function(_result){
				mStatus = 0;								
				/**On supprime l'image de loading de la requête.*/			
				ev.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.*/
					ev.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) {								
					ev.dom.element(_fieldId.str).value=extractNomLieuFromReponse(_result.dataMEVDepartAller); 	
					ev.dom.element(_fieldId.code).value=_result.dataMEVDepartAller;
					/** Mise à jour de adata et vdata du champs texte si dataMEVDepartAller2 est définie**/
					ev.dom.element(_fieldId.str).setAttribute('adata',_result.dataMEVDepartAller);				
					if(_result.dataMEVDepartAller2) { // Défini si le lieu est un aéroport.					
						ev.dom.element(_fieldId.str).setAttribute('vdata',_result.dataMEVDepartAller2);
					}					
					ev.tpl.templatePropositions.closeErrorMSG(fieldNode);
					/** Appel de la méthode de call back si définie.*/
					if(matchinCallBak) {					
						matchinCallBak();
					}
					return;
				}
				/**FIX ME voir si toujours nécessaire.*/
				ev.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 ) {	
					var bt = new togglePropPanel(_result.propositions[0],_fieldId.str,onPropositionSelected);
					/**Affichage du message d'erreur contenant le lien vers les propositions*/					
					ev.tpl.templatePropositions.displayErrorMSG(fieldNode,bt.onclick,true);																											
				}else { 
					/**Affichage du message d'erreur sans lien vers les propositions car pas de proposition.*/
					ev.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);	
				}
           },
		   /**
		    * Méthode appelée lorsque la requête est en erreur. cf .ev.requestManager.invokeEra et gestion des timeouts.
			* @param {ev.rjs.Event} e
			*/
           onError:function(e){				   
			 	/**On supprime l'image de loading de la requête.*/			
				ev.tpl.templatePropositions.closeStatusRequestImg(fieldNode);
				/**Affichage du message d'erreur sans lien vers les propositions car pas de proposition.*/
				ev.tpl.templatePropositions.displayErrorMSG(fieldNode, null,false);	
				return;  
           }
		 };

		/**
		 * 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}; 
					}
										
					ev.tpl.templatePropositions.display(_propositions,_fieldIdStr,onPropositionSelected,this.onclick);										
				}else{					
					status = false;			
					openedPanel = null;							
					ev.tpl.templatePropositions.close(_fieldIdStr);				
				}							
			};			
		}	
									
		/**
		 * 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 ev.me.PropositionManager.ValidationRequest('fieldId',_onAfter).		 
		 * 		<input type='text' onblur='request.send;'/>
		 */
		function send() {						
			if(oldOnBlur) {							
				oldOnBlur();
			}else { // Mode compatibilité autompletion basée sur des EVAL.  				
				eval(oldOnBlurEVAL);
			}
			/** 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) {				
				ev.dom.element(_fieldId.str).style.className ='';								
				ev.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 ) {				
				ev.dom.element(_fieldId.str).style.className ='';								
				ev.tpl.templatePropositions.closeErrorMSG(fieldNode);
				return;
				}
			/***/
			if(!typeof(_lieuxStr) == 'string') {				
				return false;
				}
			if(_lieuxStr.length > MAX_LIEU_NAME_LENGTH) {				
				ev.tpl.templatePropositions.displayErrorMSG(fieldNode, null,false);
				return false;
				}
		
			if(_lieuxStr.length < MIN_LIEU_NAME_LENGTH) {
				ev.tpl.templatePropositions.displayErrorMSG(fieldNode, null,false);				
				return false;
			}							
			/** Affichage du logo de status de la requête.*/
			ev.tpl.templatePropositions.displayStatusRequestImg(fieldNode);
			/**Requête va être lancée on passe le status à 1.*/																																
			mStatus = 1;
			/** Appel à .ev.requestManager.invokeEra pour lancer une requête de validation sur ERA.*/
			var url = '/vols/geo/lieux/valider.rjs?departAller=' + _lieuxStr+'&id='+_fieldId.str;
			ev.requestManager.invokeEra(request.mLocator,url, onReceive,true);	
		}	
		
		/** Getter sur la méthode interne send.*/
		this.callSend = function() {
			send();
		};
					
	};	
	
	/**
	* 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;
	}
	
	ev.log.debug("ev/me/PropositionManager.js ok");
}());