/**
 * Le gestionnaire d'id de session.<br>
 * <br>
 * Il permet de conserver le lien avec le serveur
 * qui a créé la session pour l'internaute courant.<br>
 * <br>
 * A chaque nouvelle redirection, il est
 * recommandé d'encoder le lien via la méthode
 * {ev.requestManager#encodeUrl(String)}
 * afin de dirigé chacune des requêtes HTTP vers le
 * même serveur.<br>
 * Pour effectuer une requête asynchrone (RJS, ERA)
 * il faut impérativement utiliser une des méthodes du
 * manager : invokeRjs() invokeRjsEra().<br>
 * Cela permet de s'assurer qu'au retour de chaque
 * requête, le numéro de session renvoyé par le serveur
 * est vérifié.<br>
 * Cela se fait grâce à la méthode
 * {ev.requestManager#checkSession(String)}.
 */
(function(){
	// Si les namespaces/classes nécessaires ne sont pas chargées : exception
	if(!window.ev){ window.ev={}; }
	if(!ev.me){ev.me={};}
	if(!ev.rjs){throw new Error("Le namespace 'ev.rjs' doit exister");}
	if(ev.requestManager){return 0;}
	/**
	 * Constante globale indiquant le domaine à utiliser pour les images.
	 * Si une constante URL_IMAGE est déjà définie dans la page avant l'appel 
	 * du fichier ConstructHtmlCode.js alors on utilise cette valeur.
	 * Sinon on créé la constante et on lui donne la valeur "" (chaine vide).
	 * Ce fonctionnement permet d'assurer la compatibilité avec les anciennes pages de résultat 
	 * qui utilisent un chemin relatif pour les images.
	 * Pour les affiliés on utilisera la constante const URL_IMAGE = http://www.easyvols.org pour
	 * le chemin absolu des images.
	 * NOTA : cette constante est définie dans le namespace ev.meh pour l'instant.
	 */
	ev.me.URL_IMAGE= (function(){

		if (typeof URL_IMAGE!= "undefined"){
			ev.log.info('URL_IMAGE2 existe ');
			ev.log.info(URL_IMAGE);
		    return URL_IMAGE;
		}
		else {
			ev.log.info('URL_IMAGE existe pas');
		    return "";
		}
	})(); // exécution de la procédure ici
	
	
	/** nom du cookie ou du paramètre à passer dans les url (pour maintenir le sessionId)*/
	var sIdName='ev_SID',

	/**
	 * On stocke le numéro de session.
	 * Cela permet d'éviter de fonctionner avec les cookies
	 * tant qu'on reste dans la même page.
	 */
	sId,
	/** nom du cookie ou du paramètre à passer dans les url (pour maintenir le profilId).*/	
	esvIdName='esvId',
	/**
	 * On stocke le numéro de profil.
	 * Cela permet d'éviter de fonctionner avec les cookies
	 * tant que l'on reste sur la même page.
	 * {HRS}
	 */	
	esvId,
	/**
	 * Expression régulière représentant le nom de domaine
	 * d'une URL
	 */
	RE_HOST=/http:\/\/([^:\/]*)(:[0-9]+)?/,

	/**
	 * Expression régulière permettant de trouver le début des paramètres.
	 */
	regexQuery=/^[^#]*\?/,

	/**
	 * Expression régulière représentant le '?' (pour remplacement).
	 */
	regexQueryMark=/(\?|$)/,

	/**
	 * Expression régulière représentant le '?' avec l'attribut
	 * "jsessionid=" s'il y est (pour remplacement).
	 */
	regexJSessionId=/^([^#\?;]*)(;jsessionid=[^#\?]+)?(\?|#|$)/,

	/**
	 * Expression régulière représentant le '#' (pour remplacement).
	 */
	regexAnchorMark=/(#|$)/,

	/**
	 * Expression régulière permettant de trouver l'id de session dans l'url.
	 */
	regexCookieSession=new RegExp("(?:; )?"+sIdName+"=([^;]*);?"),
	
	/**
	 * Expression régulière permettant de trouver l'id de profil dans l'url.
	 */
	regexCookieProfil=new RegExp("(?:; )?"+esvIdName+"=([^;]*);?"),

	/**
	 * Identifiant du locator utilisé par le gestionnaire
	 * d'id de sessions.
	 */
	RJS_ID='ReqMngr',

	/**
	 * Définition d'un seul Locator RJS (au cas où il y
	 * ait plusieurs requête sur le numéro de session).
	 */
	RJS_LOCATOR=new ev.rjs.Locator(12000, RJS_ID),


	/**
	 * Chemin vers la page de verification de session.
	 */
	PATH_ERA_SESSION='/session.rjs';

	/**
	 * File d'attente des requetes ERA.
	 * Utilisé pour stocker les requetes ERA tant que la session n'est pas validée.
	 */
	var queuedEraRequest = [];
	/**
	 * Permet d'encoder l'url passée en paramètre en
	 * lui ajoutant le
	 *  -  'jsessionid' permettant de
	 * contacter le bon serveur (load balancing
	 * d'Apache + ModJK) et de retrouver la session
	 * courante dans une requête asynchrone.
	 *  - le 'esvId' permettant de retrouver le profil 
	 *  associé à l'internaute. ( stocké en session ).
	 * 
	 * @param u url à encoder
	 */
	
	function encodeUrl(u){
		if(!sId){
			// rien à encoder si pas d'id de session en mémoire
			return u;
		}
		var r=u.replace(regexJSessionId, '$1;jsessionid='+sId+'$3'); 
	
		var arr=r.split('?');
		r=arr[0] + '?esvId='+esvId+'&'+arr[1]; 				
		return r;
	}
	
	/**
	 * Permet d'encoder l'ancre de l'url passée en
	 * paramètre en lui ajoutant l'id de session
	 * permettant de le conserver entre 2 pages
	 * différentes dans le cas d'un changement de
	 * domaine ou dans le cas ou les cookies sont
	 * complètement bloqués.
	 * 
	 * @param u url à encoder (si besoin)
	 * @param id numéro de session
	 * @param force permet de forcer l'encodage si l'on
	 *   souhaite le faire sans condition
	 */
	function encodeAnchor(u, id, force){
		var windowHost=window.location.host,
		urlHost=RE_HOST.test(u)&&(RegExp.$1+RegExp.$2)||null;
		// on insère / met à jour l'ancre seulement dans 1 des 2 cas suivants :
		// - si on précise le nom de domaine dans l'URL et celui-ci est différent du nom de domaine actuel (window)
		// - si les cookies sont inutilisables
		if(force||urlHost&&windowHost!==urlHost||!ev.tools.isCookieEnabled()){
			return ev.tools.setParamInAnchor(u, sIdName, id);
		}
		return u;
	}

	/**
	 * Inscrit l'id de session courant dans un cookie de session. ( durée de vie égal à la session. )
	 *  Si les cookies sont autorisés ne sont pas autorisé, aucun traitement.
	 */
	function saveSession(){		
		if(!ev.tools.isCookieEnabled()){return;}
		document.cookie=sIdName+'='+sId+';path=/';
	}

	/**
	 * Inscrit l'id de profil courant dans un cookie de domaine. Durée de vie fixée dans le code à 120 Jours
	 *  cf . 	var d = new Date( Date.parse(da) + (1000*60*60*24* 120 ));
	 *  Si les cookies sont autorisés ne sont pas autorisé, aucun traitement.
	 */	
	function saveProfil(){
		if(!ev.tools.isCookieEnabled()){return;}
		var da = new Date(); 
		var d = new Date( Date.parse(da) + ( 1000*60*60*24*120));
		document.cookie=esvIdName+'='+esvId+';path=/'+';expires='+d.toGMTString() + ";";
	}

	/**
	 * Permet de comparer le numéro de session donné
	 * à celui qui était précédemment stocké.<br>
	 * S'il est différent, le numéro de session donné
	 * sera retenu et remplacera le numéro courant
	 * (variable locale + cookie).
	 * 
	 * @param {String} sId2 id de session à comparer à la dernière session
	 *   connue du gestionnaire
	 * @return {boolean} true si la session est inchangée ; false si la session
	 *   donnée est différente ou nulle
	 */
	function chkAndUpd(sId2){
		if(!sId2){
			ev.log.error('requestManager.__chkAndUpd()> Given session Id is undefined');
			return false;
		}
		if(sId){
			if(sId2===sId){
				return true;
			}
			ev.log.warn('requestManager.__chkAndUpd()> Session Id \''+sId+'\' replaced by \''+sId2+'\'');
		}
		// Si le numéro de session donné est différent, on le garde en remplacement de l'ancien
		sId=sId2;
		saveSession();
		return false;
	}

	/**
	 * Permet de comparer le numéro de profil donné
	 * à celui qui était précédemment stocké.<br>
	 * S'il est différent, le numéro de profil donné
	 * sera retenu et remplacera le numéro courant
	 * (variable locale + cookie).
	 * 
	 * @param {String} _esvId id de profil à comparer au dernier numéro de profil. 
	 *   connue du gestionnaire
	 * @return {boolean} true si le profil est inchangée ; false si le profil
	 *   donnée est différente ou nulle
	 *   {HRS}
	 */
	function chkAndUpdesvId(_esvId){
		if(!_esvId){
			ev.log.info('requestManager.chkAndUpdesvId()> Given profil Id is undefined');
			return false;
		}
		if(esvId){
			if(_esvId===esvId){	
				return true;
			}
			ev.log.warn('requestManager.chkAndUpdesvId()> Profil Id \''+esvId+'\' replaced by \''+_esvId+'\'');
		}
		if(_esvId !='none') {
			esvId=_esvId;
			// ... et on le sauvegarde
			saveProfil();	
		}		
		
		return false;
	}

	/**
	 * Permet d'ajouter le bon identifiant RJS
	 * pour une requête sur l'ERA via le locator
	 * local.
	 * 
	 * @param u url à complèter
	 */
	function appendRjsId(u){
		return u&&(regexQuery.test(u)&&u.replace(regexQueryMark, '?id='+RJS_ID+'&')||u.replace(regexAnchorMark, '?id='+RJS_ID+'$1'));
	}

	/**
	 * locator = objet qui definit le time out que l on veut sur la requete et le namespace dans lequelle la reponse sera donné
	 * path = url lance sur le era (requette sur le server ERA)
	 * callback = methode appeler apres la reponse du server
	 * force = type booelan, force le cache si true. 
	 * 
	 * @see ev.requestManager#invokeRjs(locator, path, callback, force)
	 */
	function invokeRjs(l, p, c, f)
	{
		if(typeof(l)==='string'){
			// si l(ocator) est un string, c'est qu'on a choisi de ne pas donner de locator, donc on décale les params
			f=c;
			c=p;
			p=appendRjsId(l); // pour le (p)ath (chemin), on doit préciser l'ID de la requête RJS
			// et le locator est celui par défaut
			l=RJS_LOCATOR;
		}
		ev.rjs.simpleInvoke(l, ev.rjs.URL_ROOT+p, function(r){
			chkAndUpd(r&&r.sessionId);
			chkAndUpdesvId(r&&r.profilId);
			if(typeof(c)==='function'){
				c(r);
			}
		}, f);
	}

	/**
	 * locator = objet qui definit le time out que l on veut sur la requete et le namespace dans lequelle la reponse sera donné
	 * path = url lance sur le era (requette sur le server ERA)
	 * callback = methode appeler apres la reponse du server
	 * force = type booelan, force le cache si true. 
	 * 
	 * @see ev.requestManager##invokeEra(locator, path, callback, force)
	 */
	function invokeEra(l, p, c, f){
		
		if(typeof(l)==='string'){
			// si l(ocator) est un string, c'est qu'on a choisi de ne pas donner de locator, donc on décale les params
			f=c;
			c=p;
			p=appendRjsId(l); // pour le (p)ath (chemin), on doit préciser l'ID de la requête RJS
			// et le locator est celui par défaut
			l=RJS_LOCATOR;
		}
		ev.rjs.simpleInvoke(l, encodeUrl(ev.rjs.URL_ERA_ROOT+p),
			{
				onSucces:function(r){
					chkAndUpd(r&&r.sessionId);
					chkAndUpdesvId(r&&r.profilId);
					if(!c){
						ev.log.info("ev.requestManager_Pas de callback défini pour "+p);
					}
					else if(typeof(c)==='function'){
						c(r);
					}
					else if(typeof(c.onSucces)==='function'){
						c.onSucces(r);
					}
					else{
						ev.log.error("ev.requestManager#invokeEra: Le callback doit être une function ou un objet contenant la propriété 'onSucces'");
					}
				},
				//appeler lorsqu'il y a un timeOut dans la reponse  
				onError:c&&c.onError
			},
		 f);
	}

	/**
	 * Objet de stockage de requete
	 */
	function QueuedRequest(locator, path, callback, force) {
		this.call = function() {
			invokeEra(locator, path, callback, force);
		};
	}

	/**
	 * Parcours la pile de requete mise en attente, et les execute les unes après les autres.	 
	 */
	function makeQueued(){
		for(var i=0;i<queuedEraRequest.length;i++) {
			queuedEraRequest[i].call();
		}
		queuedEraRequest = [];
	}




	
	/**
	 *  Facilite l'accès au service ERA Session.
	 *  Implémente la couche de retry.	 
	 *  Méthode disponible : 
	 *  	- getSession: Lance une requête de validation / récupération de session ID. Si un numéro 
	 *  de session est disponible, il est ajouté en parametre de l'url.
	 *  La réponse contient l'id de session valide. Celui donnée en parametre si la session est encore valide
	 *  ou un nouveau numéro si la session est expirée.
	 *  Pour l'utiliser
	 *  	=> dao.getSession({listener:this,parentController:this});
	 *  	listener : fonction à appeler à réception de la réponse, ou quand la requête échou( timeout, pb servers etc )
	 *  	parentController: Context dans lequel le listener doit être appelé.
	 *  		cf . notion JS, context d'appel d'une fonction.
	 * @param {Object} _param.listener - fonction à appeler lorsque la requête est terminée ou en cas d'échec.
	 * @param {Object} _param.parentController - fonction appelante
	 */	
	dao={					  
		getSession:function(_param) {
			// Pointer vers l'instance de l'objet- Permet de simplifier la lecture.
			var This=this;
			// Nombre de retry effectué pour la requete de session.
			var nbRequest=0;
			// Variable contenant une instance de locator. est utilisé dans le cas ou l'on 
			// doit relancer une requête suite à un timeout. L'on créer un nouveau locator
			// initialisé avec une valeure de timeout plus importante.
			// @deprecated
			var locator=null;
			// test si tous les parametres sont bien présents.			
			if(!_param.listener) {				
				return;
			}
			if(!_param.parentController) {				
				return;
			}
			// Méthode interne appelée lorsque le chargement de la requête est terminé.
			this.onSucces=function(r) {								
				// Création d'une chaine contenant plusieurs informations sur le client ( navigateur ).												
				_param.listener.call(_param.parentController,r);								
			};
			// Méthode interne appelé lorsque le timeout de la requête est atteint. On relance la requête tant que l'on a pas atteint le nombre 
			// maximum de tentative.
			this.onError=function(r) {				
				var MAX_TIMEOUT_REQUEST=3;
				nbRequest=nbRequest+1;				
				if(nbRequest>= MAX_TIMEOUT_REQUEST) {				
					_param.listener.call(_param.parentController,{exception:'MAX_RETRY_REACHED'});					
					return;
				}							
				This.send(true);
			};
			/**
			 * 
			 * @param {boolean} retry boolean indiquant si l'on doit utiliser le locator par défaut ou un nouveau lcoator ayant une durée
			 * de timeout plus importante.
			 */
			this.send=function(retry) {								
				if(retry && retry===true) {											
					var lor=new ev.rjs.Locator(nbRequest*8000, 'sessionRR');
					invokeEra(lor,PATH_ERA_SESSION+'?id=sessionRR', {onSucces:This.onSucces,onError:This.onError},true);
				}else {									
					invokeEra(PATH_ERA_SESSION, {onSucces:This.onSucces,onError:This.onError});	
				}								
			};						
			this.send();
		}
	};
	/**
	 * fonction appelée lorsque la requête de session est terminée.
	 * onsuccess ou onError. 
	 * @param {Object} r
	 */
	function onSessionRestored(r) {
		if(r.exception) {			
			var flagStr='';
			if(navigator) {
				if(navigator.appCodeName) {flagStr=flagStr.concat('|Codee='+navigator.appCodeName);}
				if(navigator.appVersion) {flagStr=flagStr.concat('|Vers='+navigator.appVersion);}
				if(navigator.platform) {flagStr=flagStr.concat('|platform='+navigator.platform);}
			}					
			ev.requestManager.errorLogger('SessionRetrievingFailed'+'>'+flagStr);			
			return;
		}			
		chkAndUpd(r.sessionId);
		chkAndUpdesvId(r.profilId);				
		ev.requestManager.invokeEra=function(locator, path, callback, force){
			invokeEra(locator, path, callback, force);
		};					
		makeQueued();			
	}
	/**
	 * Récupère l'identifiant de la session courante
	 * ou de la nouvelle session.
	 * + Récupère l'identifiant de profil.
	 */
	function restoreSession(){
		// on recupere l'id de session dans l'url de la page s'il existe
		sId=ev.tools.getAnchorParameters&&ev.tools.getAnchorParameters(window.location.href)[sIdName];
		// sinon, on recupere l'id de session dans un cookie s'il existe
		if(!sId&&ev.tools.isCookieEnabled()){
			sId=regexCookieSession.test(document.cookie)&&RegExp.$1;
		}
		// Si une session est dans l'url ou les cookies. on la sauvegarde
		if(sId){		
			saveSession();
		}
		// sinon, on recupere l'id de profil dans un cookie s'il existe	
		if(!esvId&&ev.tools.isCookieEnabled()){
			esvId=regexCookieProfil.test(document.cookie)&&RegExp.$1;
		}
		// on déclare une fonction de callback (pour exécution après l'appel à l'ERA)
		dao.getSession({parentController:this,listener:onSessionRestored});		
	}
	/**
	 * Déclaration de la partie visible (publique)
	 * du gestionnaire d'id de session.
	 */
	ev.requestManager={
	
		/**
		 * Permet d'encoder l'url passée en paramètre pour
		 * retrouver la session courante dans la page
		 * suivante (ou requête asynchrone).
		 * 
		 * @param u url à encoder
		 */
		encodeUrl: function(u){
			return encodeUrl(u);
		},

		/**
		 * Permet d'encoder l'url passée en paramètre en
		 * utilisant l'ancre (pour éviter d'entrer en
		 * conflit avec des systèmes de session tiers ;
		 * ex: affiliations).
		 * L'encodage ne se fait que si le nom de domaine
		 * de la requête est différent de celui de la page
		 * courante, ou si les cookies ne sont pas accepté.
		 * Dans les autres cas, l'identifiant de la session
		 * sera conservé dans un cookie.
		 * 
		 * @param u url à encoder
		 */
		encodeAnchor: function(u){
			return encodeAnchor(u, sId);
		},

		/**
		 * Méthode permettant d'invoquer une requête en
		 * RJS en prenant en compte l'information sur la
		 * session courante.<br>
		 * L'invocation de RJS est considérée comme
		 * "stateless", c'est-à-dire qu'on ne tient pas
		 * compte des sessions.<br>
		 * 
		 * @see #invokeEra(locator, url, callback, force)
		 *
		 * @param {ev.rjs.Locator} locator [optionel] Locator spécifique à utiliser pour la requête
		 * @param {String} path chemin du service demandé (avec les paramètres à traiter par le service)
		 * @param {Function} callback [optionel] fonction de callback à appeler lorsque le service renvoi un résultat
		 * @param {Boolean} force [optionel] parametre permettant de préciser
		 *   s'il on souhaite absoluement faire la requete (true) ou s'il on
		 *   souhaite utiliser le cache (false) lorsque la requête a déjà été
		 *   exécutée
		 */
		invokeRjs: function (locator, path, callback, force){
			invokeRjs(locator, path, callback, force);
		},

		/**
		 * Méthode permettant d'invoquer une requête en
		 * RJS sur le serveur ERA. (Encodage d'URL avec
		 * id de session + vérification de l'id renvoyé
		 * si présent dans la réponse)
		 * 
		 * @see #invokeRjs(locator, url, callback, force)
		 *
		 * @param {ev.rjs.Locator} locator [optionel] Locator spécifique à utiliser pour la requête
		 * @param {String} path chemin du service demandé (avec les paramètres à traiter par le service)
		 * @param {Function} callback [optionel] fonction de callback à appeler lorsque le service renvoi un résultat
		 * @param {Boolean} force [optionel] parametre permettant de préciser
		 *   s'il on souhaite absoluement faire la requete (true) ou s'il on
		 *   souhaite utiliser le cache (false) lorsque la requête a déjà été
		 *   exécutée
		 */
		invokeEra: function (locator, path, callback, force){											
			var temp = new QueuedRequest(locator, path, callback, force);
			queuedEraRequest[queuedEraRequest.length] = temp;	
		},
		
		/**
		 * Méthode de relance des requêtes rjs qui ont aboutit sur une erreur.
		 * Pour l'url donnée, on relance une requête rjs avec un ev.rjs.Locator avec un timeOut de plus
		 * en plus long tout en sachant qu'on limite le nombre autorisé d'appel à nMax 
		 * @param {String} url [obligatoire]
		 * @param {function} callback [obligatoire]
		 * @param {Integer} nbError [obligatoire] indique le nombre d'erreur qu'il y a déjà eu pour cette requête
		 * @param {String} locatorName [optionel] nom du locator à utiliser (par défaut RJS_ID)
		 */
		retry: function (url, callback, nbError, locatorName){
			if(!url||typeof(url)!=="string"){
				ev.log.error("Le paramètre url est obligatoire et doit être une url valide (url="+url+")");
				return;
			}
			if(!nbError||typeof(nbError)!=="number"){
				ev.log.error("Le paramètre nbError est obligatoire et doit être un nombre valide (nbError="+nbError+")");
				return;
			}
			var nMax=3; //TODO: peut-être le passer en paramètre optionnel
			if(nbError>nMax) {
				this.errorLogger("requestFailed_"+url);
				return;			
			}
			var name=RJS_ID;
			if(locatorName&&typeof(locatorName)==="string"){
				name=locatorName;
			}					
			//ev.log.error("ev.requestManager#retry: "+nbError+"_target_url="+url);			
			//ev.requestManager.errorLogger('retry_'+nbError+'_'+url);							
			var locator=new ev.rjs.Locator(5000*(1+nbError), name);			
			ev.rjs.simpleInvoke(locator, url, callback, true);
			
		}
		/**
		 * Définition d'une méthode de log, tapant sur le service ERA /erreur.rjs, mais sans utiliser de RJS.
		 * La requête est executée en créer une balise img et en settant son source sur l'URL sur service ERA.
		 */		 			 
		,errorLogger:function(errorMessage) {				
			var n=document.createElement('img');				
			if(window.sessionId03485 && window.sessionId03485.length>0) {					
				var server='';
				var tmp=window.sessionId03485.split('.');
				if(tmp.length==2) {
					server=tmp[1];
				}
				if(server && server.length>0) {
					n.setAttribute("src",ev.rjs.URL_ERA_ROOT+"/erreur.rjs;jsessionid=."+server+"?erreur="+encodeURIComponent(errorMessage));
					n=null;
					return;	
				}											
			}
			n.setAttribute("src",ev.rjs.URL_ERA_ROOT+"/erreur.rjs;jsessionid="+sId+"?erreur="+encodeURIComponent(errorMessage));					
			n=null;
		},
		/**
		 * Définition d'une méthode de log, tapant sur le service ERA /flag.js, mais sans utiliser de RJS.
		 * La requête est executée en créer une balise img et en settant son source sur l'URL sur service ERA. 
		 */
		flagLogger:function(flagMessage) {		
			var n=document.createElement('img');	
			if(window.sessionId03485 && window.sessionId03485.length>0) {					
				var server='';
				var tmp=window.sessionId03485.split('.');
				if(tmp.length==2) {
					server=tmp[1];
				}
				if(server && server.length>0) {
					n.setAttribute("src",ev.rjs.URL_ERA_ROOT+"/flag.rjs;jsessionid=."+server+"?flag="+encodeURIComponent(flagMessage));	
					n=null;
					return;	
				}											
			}				
			n.setAttribute("src",ev.rjs.URL_ERA_ROOT+"/flag.rjs;jsessionid="+sId+"?flag="+encodeURIComponent(flagMessage));			
			n=null;
		}				
	};		
	
	// initialisation
	restoreSession();	
	ev.log.debug('ev.requestManager ok');
	ev.tools.onFileLoad('ev/requestManager.js');
}());