

/**
 * 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() {
	// raccourci vers l'objet window de la page
	var window = this,
			// raccourci vers window.document
			document = window.document,
			// raccourci vers la package principal d'easyvoyage (ev)
			ev = window.ev,
			// synonymes de true et false
			FALSE = !ev, TRUE = !FALSE,
			// raccourci vers le package ev.rjs
			RJS,
			// raccourci vers le package ev.me
			ME,
			// raccourci vers le package ev.log
			LOG,
			// alias de undefined
			UNDEFINED;

	if (FALSE) {throw 'requestManager#<init>: Needs ev.core module !';}

	//Si ev.requestManager est déjà défini, on sort
	if (ev.requestManager) {return;}

	RJS = ev.rjs;
	ME = ev.me || (ev.me = {});// FIXME [ygally] devrait pas etre là
	LOG = ev.log;

	if (!RJS) {throw "Le namespace 'ev.rjs' doit exister";}
	if (!ME) {throw "Le namespace 'ev.me' doit exister";}
	if (!LOG) {throw "Le namespace 'ev.log' doit exister";}

	// FIXME [ygally] devrait pas etre là... utiliser ev.path.img
	/**
	 * Si une constante URL_IMAGE est déjà définie dans la page avant l'appel
	 * Constante globale indiquant le domaine à utiliser pour les images.
	 * 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.
	 */
	ME.URL_IMAGE = (function() {
		if (window.URL_IMAGE !== UNDEFINED) {
			LOG.info('requestManager#<init>: URL_IMAGE existe -> ' + window.URL_IMAGE);
			return window.URL_IMAGE;
		}
		else {
			LOG.info('requestManager#<init>: 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',
			// Nom du cookie contenant le code client de l'internaute. ( le code client est donné par le paramèter clientId dans l'URL ).
			esvCodeClientName = 'esvCodeClient',
			// Contient la valeure du code Client de l'internaute**/
			clientId,
			// 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 + '=([^;]*);?'),
			// Expression régulière permettant de trouver le code client dans les cookies.
			regexCookieCodeClient = new RegExp('(?:; )?' + esvCodeClientName + '=([^;]*);?'),
			// Fonction de récupération d'une nouvelle session
			getSession,
			// Fonction à exécuter après la récupération d'une nouvelle session
			onSessionRestored,
			// Fonction qui contiendra l'implémentation de l'invocation d'une requête sur l'ERA
			invokeEraX,
			// File d'attente des requetes ERA.
			// Utilisé pour stocker les requetes ERA tant que la session n'est pas validée.
			queuedEraRequest = [],
			// 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 RJS.Locator(12000, RJS_ID),
			// Chemin vers la page de verification de session.
			PATH_ERA_SESSION = '/session.rjs',
			PATH_ERA_TRACKING = '/tracking.rjs',
			// Gestion de séc ur ité
			OT_NOK = TRUE, // true par défaut [dit si c'est pas encore OK]
			NEED_OT, // undef par défaut, soit false [dit si on doit le rendre OK]
			EvKey; // undef par défaut, [si définit, c'est qu'on a déjà envoyé une requête avec le code]

	/**
	 * Helper. renvoi la valeur du cookie de nom strName.
	 * @param {Object} strName nom du cookie.
	 */
	function getCookieValue(strName) {
		var reg = new RegExp('(?:; )?' + strName + '=([^;]*);?');
		return reg.test(document.cookie) && RegExp.$1;
	}

	/**
	 * Permet d'ajouter le paramètre donné à l'URL donnée.
	 *
	 * @param {?*=} url url à complèter.
	 * @param {?string=} param nom du param à rajouter.
	 * @param {?string=} value valeur du param à rajouter (défaut '').
	 */
	function appendParam(url, param, value) {
		if (typeof(url) === 'string' && param) {
			param = param + '=' + (value || '');
			return url && (regexQuery.test(url) && url.replace(regexQueryMark, '?' + param + '&') || url.replace(regexAnchorMark, '?' + param + '$1'));
		}
		return url;
	}

	/**
	 * Extrait le nom du service ERA de l'url.
	 * exemple d'url http://test.era.lcom/fr_FR/vols/recherche/MEV/demarrer.rjs;jsessionid=C192A304FBD2A057E597902351FEF18F.oxford?esvId=1279180619274865065118055967.oxford&id=ReqMngr
	 * Résultat obtenu => fr_FR/vols/recherche/MEV/demarrer.rjs;jsessionid=C192A304FBD2A057E597902351FEF18F.oxford
	 * @param {!string} str url de la requête du service ERA.
	 */
	function extractServiceName(str) {
		var out = '';
		var serviceName = str.split('era.lcom');
		if (serviceName) {
			var tmp = serviceName[0].split('?');
			out = tmp[0];
		}
		LOG.info('requestManager#_extractServiceName(): service = ' + out + ' [from ' + str + ']');
		return out;
	}

	/**
	 * Extrait les parametres du service ERA de l'url.
	 * exemple d'url http://test.era.lcom/fr_FR/vols/recherche/MEV/demarrer.rjs;jsessionid=C192A304FBD2A057E597902351FEF18F.oxford?esvId=1279180619274865065118055967.oxford&id=ReqMngr
	 * Résultat obtenu => esvId=1279180619274865065118055967.oxford&id=ReqMngr
	 * @param {Object} str url de la requête du service ERA.
	 */
	function extractParameters(str) {
		var out = '';
		var serviceName = str.split('era.lcom');
		if (serviceName) {
			var tmp = serviceName[0].split('?');
			if (tmp.length > 1) {
				out = tmp[1];
			}else {
				out = '';
			}
		}
		LOG.info('requestManager#_extractParameters(): service params = ' + out + ' [from ' + str + ']');
		return out;
	}

	/**
	 * Remplace la valeure du parametre paramName de l'url donnée.
	 *  La nouvelle valeure est donnée par le parametre newValue.
	 * Algo :
	 * - Split de l'url pour récuperer une chaine contenant tous les paramètres.
	 * - Split de la chaine de parametres pour construire un tableau contenant les parametres.
	 * - recherche du parametre ID
	 * - Set de la nouvelle valeure.
	 * - Reconstruction de l'url avec les nouveaux parametres.
	 *
	 * @param {string} url URL du service ERA à intérroger.
	 * @param {string} newValue Nouvelle valeur.
	 * @param {string} paramName Nom du paramètre à qui va être attribué la nouvelle valeur.
	 */
	function replaceIdValuesBy(url,newValue,paramName) {
		var tab_parameters, str_parameters, i;
		var temp = url.split('?');
		if (temp.length < 2) {
			return null;
		}
		str_parameters = temp[1];
		// Lecture des parametres de l'url.
		tab_parameters = str_parameters.split('&');
		if (! tab_parameters) {
			LOG.error('requestManager.replaceIdValuesBy_no_rul_parameters');
			return null;
		}
		if (!tab_parameters.length) {
			LOG.error('requestManager.replaceIdValuesBy_no_rul_parameters');
			return null;
		}
		if (!paramName) {
			LOG.error('requestManager.replaceIdValuesBy_no_rul_parameters');
			return null;
		}

		for (i = 0; i < tab_parameters.length; i++) {
			var name_value = tab_parameters[i];
			var name = name_value.split('=')[0];
			if (name === paramName) {
				tab_parameters[i] = paramName + '=' + newValue;
			}
		}
		var out = '';
		var separator = '';
		for (i = 0; i < tab_parameters.length; i++) {
			if (i !== 0) {
				separator = '&';
			}
			out = out + separator + tab_parameters[i];
		}
		url = temp[0] + '?' + out;
		return url;
	}

	/**
	 * 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 {!string} url url à encoder.
	 */

	function encodeUrl(url) {
		if (!sId) {
			// rien à encoder si pas d'id de session en mémoire
			return url;
		}
		url = url.replace(regexJSessionId, '$1;jsessionid=' + sId + '$3');

		var arr = url.split('?');
		url = arr[0] + '?esvId=' + esvId + '&' + arr[1];

		// Gestion de la s*
		if (OT_NOK && NEED_OT) {
			// Si la session n'a pas encore été identifiée, il faut rajouter la c lé cr yp tée dans les paramètres
			// - c lé cr-yp tée
			EvKey = ev.securityManager.encrypt('78abT1p2B_CXoSmEqkl-rOefLM5Ru9HI5QNx6vwDhVWyJKsUtYZ0gF3zAnPcdGij', '3wIFzGG2E7m_ExVHt77S2V8w', sId);
			// - ajout du paramètre
			url = appendParam(url, 'EvKey', EvKey);
			//      LOG.warn('requestMngr#_encodeUrl(): url enc -> url = '+url);
		}

		return url;
	}

	// FIXME à activer quand c'est utile
	//  /**
	//   * Surcharge de encodeUrl.
	//   * Permet d'encoder les URL en utilisant des paramètres à la place des ancres.
	//   * A utiliser pour tous les liens pointant vers les pages dynamiques.
	//   *
	//   * @param {!string} u url à encoder.
	//   * @param {!boolean} type true s'il s'agit d'une page cachée, sinon false (page de destination statique=true ; page de destination dynamique=false).
	//   * @param {boolean=} force (optionnel) true pour forcer l'ajout du paramètre dans l'url.
	//   */
	function encodeSessionAndProfilId(url, type, force) {
		var windowHost = window.location.host,
				urlHost = RE_HOST.test(url) && (RegExp.$1 + RegExp.$2) || null, out;

		// Liens vers
		if (type === TRUE) {
			// 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()) {
				out = url;
				if (sIdName) {
					out = ev.tools.setParamInAnchor(url, sIdName, sId);
				}
				if (esvIdName) {
					out = ev.tools.setParamInAnchor(out, esvIdName, esvId);
				}

				return out;
			}
			return url;
		//Lien vers page dynamique.
		}else {
			if (force || urlHost && windowHost !== urlHost || !ev.tools.isCookieEnabled()) {
				if (url.indexOf('?') !== -1) {
					out = url + '&' + sIdName + '=' + sId;
					out = out + '&' + esvIdName + '=' + esvId;
				}else {
					out = url + '?' + sIdName + '=' + sId;
					out = out + '&' + esvIdName + '=' + esvId;
				}
				return out;
			}
			return url;
		}
	}

	// FIXME à activer quand c'est utile
	//  /**
	//   * true cachée
	//   * false
	//   * @param {Object} node
	//   */
	function encodeLink(node) {
		if (node.href) {
			node.href = encodeSessionAndProfilId(node.href, true, false);
		}
	}

	function encodeLinkUrl(url) {
		return encodeSessionAndProfilId(url, true, false);
	}
	ev.encodeLink = encodeLink;
	ev.encodeLinkUrl = encodeLinkUrl;

	/**
	 * 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 {!string} u url à encoder (si besoin).
	 * @param {boolean=} force permet de forcer l'encodage si l'on
	 *   souhaite le faire sans condition.
	 */
	function encodeAnchor(u, 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()) {
			var out = ev.tools.setParamInAnchor(u, sIdName, sId);
			out = ev.tools.setParamInAnchor(out, esvIdName, esvId);
			return out;
		}
		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() + ';';
	}

	/**
	 * Enregister le code client dans un cookie de session.
	 */
	function createCodeClientCookie() {
		if (!ev.tools.isCookieEnabled()) {return;}
		document.cookie = esvCodeClientName + '=' + clientId + ';path=/';
	}

	/**
	 * Objet de stockage de requete
	 *
	 * @constructor
	 */
	function QueuedRequest(locator, path, callback, force) {
		this.invoke = function() {
			invokeEraX(locator, path, callback, force);
		};
	}

	/**
	 * Parcours la pile de requete mise en attente, et les execute les unes après les autres.
	 */
	function makeQueued() {
		while (queuedEraRequest.length) {
			queuedEraRequest[0].invoke();
			queuedEraRequest.shift();
		}
	}

	/**
	 * Fonction d'initialisation de la méthode invokeEra
	 * dans le requestManager.<br>
	 * <b>NOTA :</b> après cette étape, on attends que la
	 * session soit établie.
	 */
	function initInvokeMethod() {
		/**
		 * 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 {Object} locator [optionel] ev.rjs.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.
		 */
		ev.requestManager.invokeEra = function(locator, path, callback, force) {
			queuedEraRequest[queuedEraRequest.length] = new QueuedRequest(locator, path, callback, force);
		};
	}

	/**
	 * 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();
		}

		//alert('0-------esvId='+esvId);
		// on déclare une fonction de callback (pour exécution après l'appel à l'ERA)
		getSession({
			parentController: window,
			listener: onSessionRestored
		});
	}

	/**
	 * 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.
	 * @param {Object=} result resultat de la requete (optionnel).
	 * @return {boolean} true si la session est inchangée ; false si la session
	 *   donnée est différente ou nulle.
	 */
	function chkAndUpd(sId2, result) {
		if (!sId2) {
			LOG.error('requestManager.__chkAndUpd()> Given session Id is undefined');
			if (!OT_NOK) {

				NEED_OT = !(OT_NOK = TRUE);
				LOG.warn('requestManager.__chkAndUpd()> Last except: ' + result.exception);
				result.exception = 'Session Expired [' + sId + ']';
				LOG.warn("requestManager.__chkAndUpd()> On remet en place la file d'attente...");
				initInvokeMethod();
				LOG.warn('requestManager.__chkAndUpd()> On relance la demande de session...');
				restoreSession();
			}
			return FALSE;
		}
		if (sId2 === 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.XXXXX') {
			LOG.error('requestManager.__chkAndUpd()> Given session Id is Not Authorized');
			return FALSE;
		}
		if (sId) {
			if (sId2 === sId) {
				LOG.info('requestMngr#__chkAndUpd(): session id déjà bon');

				// Si la session est bonne, on doit vérifier le contenu de la dernière requete pour check l'ot
				if (OT_NOK && NEED_OT) {
					if (result && result.information) {
						LOG.info('requestMngr#__chkAndUpd(): information = ' + result.information);
						// FIXME attention l'attribut 'information' et/ou le terme AUTHENTIFICATION OK risquent de changer
						if (result.information === 'AUTHENTIFICATION OK') {
							OT_NOK = FALSE;
						}
					}
				}
				// On renvoie "session inchangée"
				return TRUE;
			}
			LOG.warn('requestManager#__chkAndUpd(): Session Id \'' + sId + '\' replaced by \'' + sId2 + '\'');
		}
		// Si le numéro de session donné est nouveau ou différent, on le garde en remplacement de l'ancien
		sId = sId2;

		// si c'est une nouvelle session, on indique que la prochaine requête vers l'ERA doit être otantifyaie
		NEED_OT = TRUE;
		LOG.warn('requestMngr#__chkAndUpd(): need ot specified');

		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) {
			LOG.info('requestManager.chkAndUpdesvId()> Given profil Id is undefined');
			return FALSE;
		}
		if (esvId) {
			if (_esvId === esvId) {
				return TRUE;
			}
			LOG.warn('requestManager.chkAndUpdesvId()> Profil Id \'' + esvId + '\' replaced by \'' + _esvId + '\'');
		}
		if (_esvId !== 'none') {
			esvId = _esvId;
			// ... et on le sauvegarde
			saveProfil();
		}

		return FALSE;
	}

	/**
	 * @see ev.requestManager#invokeRjs(locator, path, callback, force)
	 *
	 * @param {!(Object|string)} locator [optionel] ev.rjs.Locator spécifique
	 *   à utiliser pour la requête.
	 * @param {?(string|Object|Function)=} path chemin du service demandé
	 *   (avec les paramètres à traiter par le service).
	 * @param {?(Object|Function|boolean)=} 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.
	 */
	function invokeRjs(locator, path, callback, force) {
		if (typeof(locator) === '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
			force = callback;
			callback = path;
			path = appendParam(locator, 'id', RJS_ID); // pour le path (chemin), on doit préciser l'ID de la requête RJS
			// et le locator est celui par défaut
			locator = RJS_LOCATOR;
		}
		RJS.simpleInvoke(locator, RJS.URL_ROOT + path, function(r) {
			chkAndUpd(r && r.sessionId, r);
			chkAndUpdesvId(r && r.profilId);
			if (typeof(callback) === 'function') {
				callback(r);
			}
		}, force);
	}



	/**
	 * Redéfini la méthode invokeEra en intégrant une gestion des timeout de requête. Pour cela lorsque la requête est en timeout,
	 * elle est relancée avec un locator différent ayant une durée de timeout plus importante.
	 * FIXME - Passer à un passage des paramètres par objet {l:l,p:p etc...} ?? =>  invokeEraX = function (params){.
	 * @param {!(Object|string)} locator [optionel] ev.rjs.Locator spécifique
	 *   à utiliser pour la requête.
	 * @param {?(string|Object|Function)=} path chemin du service demandé
	 *   (avec les paramètres à traiter par le service).
	 * @param {?(Object|Function|boolean)=} 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.
	 */
	invokeEraX = function(locator, path, callback, force) {
		// Contient le nombre de tentative de requête.
		var nbRequest = 0,
				// Nombre max de retry à effectuer en cas de timeout.
				MAX_RETRY = 3;
		// On permet de ne pas donner de locator en parametre, si c'est le cas on doit décaler les paramètres.
		if (locator && typeof(locator) === 'string') {
			force = callback;
			callback = path;
			path = appendParam(locator, 'id', RJS_ID); // pour le path (chemin), on doit préciser l'ID de la requête RJS
			locator = RJS_LOCATOR;
		}
		// On extrait le nom du service sur lequel on fait une requête.
		var serviceName = extractServiceName(path),
				// On extrait les parametres de l'url.
				parameters = extractParameters(path);

		/**
		 * Méthode d'envoi de la requête, se base sur la variable nbRequest pour savoir si l'on utilise un nouveau locator, celui par défaut
		 * ou celui donné en paramètre.
		 * Si l'on redéfini un locator, on met à jour le parametre id de l'url afin de synchroniser avec le nouveau locator.
		 *
		 * @param {!Function} onSuccess fonction à exécuter si tout se passe bien sur l'envoi de la requête.
		 * @param {!Function} onError fonction à exécuter en cas d'erreur sur l'envoi de la requête.
		 */
		function send(onSuccess, onError) {
			nbRequest++;
			if (nbRequest === 1) {

				// FIXME modif onSuccess en cas d exception
				//RJS.simpleInvoke(locator, encodeUrl(RJS.URL_ERA_ROOT + path), {onSuccess: onSuccess, onError: onError}, force);
				RJS.simpleInvoke(locator, encodeUrl(RJS.URL_ERA_ROOT + path), {
					onSuccess: function(r) {
						if (r.exception !== 'Engine Acces Denied') {
							onSuccess(r);
						}
						//            else {
						//              // FIXME do something
						//            }
					},
					onError: onError}, force);
			}
			else {
				var d = new Date(),
						NEW_RJS_ID = 'RETRY' + d.getTime(),
						locatorTmp = new RJS.Locator(8000 * (nbRequest), NEW_RJS_ID);
				path = replaceIdValuesBy(path, NEW_RJS_ID, 'id');

				// FIXME modif onSuccess en cas d exception
				//RJS.simpleInvoke(locatorTmp, encodeUrl(RJS.URL_ERA_ROOT + path), {onSuccess: onSuccess, onError: onError}, force);

				RJS.simpleInvoke(locatorTmp, encodeUrl(RJS.URL_ERA_ROOT + path), {
					onSuccess: function(r) {
						if (r.exception !== 'Engine Acces Denied') {
							onSuccess(r);
						}
						//            else {
						//              // FIXME do something
						//            }
					}, onError: onError}, force);
			}
		}

		/**
		 * Méthode appelée lorsque la réponse est correctement récupérée.
		 *  - Vérification du numéro de session tomcat ERA de la réponse avec celui stocké en local.
		 *  - Vérification du numéro de profil de la réponse avec celui stocké en local.
		 *  - appel de la méthode onSucess si définie.
		 *  - Log sur ERA si plusieurs requêtes ont été nécessaire.
		 */
		function onSuccess(r) {
			chkAndUpd(r && r.sessionId, r);
			chkAndUpdesvId(r && r.profilId);
			if (nbRequest > 1 && serviceName) {
				ev.requestManager.flagLogger('service>' + serviceName + '>' + parameters + '>' + nbRequest);
			}
			if (!callback) {
				LOG.info('ev.requestManager_Pas de callback défini pour ' + path);
			}
			else if (typeof(callback) === 'function') {
				callback(r);
			}
			else if (typeof(callback.onSuccess) === 'function') {
				callback.onSuccess(r);
			}
			else {
				LOG.error("ev.requestManager#invokeEra: Le callback doit être une function ou un objet contenant la propriété 'onSuccess'");
			}

		}

		/**
		 * Méthode appelée lors d'un timeout sur la requête.
		 * - Test si on a atteint le maximum de tentative.
		 * - si oui, on appel la méthode onError donnée en paramètre. Celle ci doit être définit au niveau de chaque appel
		 * à la méthode invokeERA et définit le process fonctionnel à dérouler en cas d'erreur.
		 * - si non, on relance la requête avec un locator différent.
		 *
		 * @param {Object=} err (optionnel) erreur reçue.
		 */
		function onError(err) {
			if (nbRequest < MAX_RETRY) {
				send(onSuccess, onError);
				return;
			}
			ev.requestManager.errorLogger('serviceError_' + serviceName + '>' + parameters + '>' + nbRequest);
			if (callback && callback.onError) {
				callback.onError(err);
			}
		}

		// premier appel à send
		send(onSuccess, onError);
	};

	/**
	 * Méthode d'envoi de requête vers l'ERA. N'implémentte pas de système de gestion des timeout.
	 * Si la requête pars en timeout la métode onerror du callback est appelé.
	 * @see ev.requestManager##invokeEra(locator, path, callback, force)
	 * @param {!(Object|string)} locator [optionel] ev.rjs.Locator spécifique
	 *   à utiliser pour la requête.
	 * @param {?(string|Object|Function)=} path chemin du service demandé
	 *   (avec les paramètres à traiter par le service).
	 * @param {?(Object|Function|boolean)=} 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.
	 */
	function invokeEraNoX(locator, path, callback, force) {
		if (typeof(locator) === 'string') {
			// si locator est un string, c'est qu'on a choisi de ne pas donner de locator, donc on décale les params
			force = callback;
			callback = path;
			path = appendParam(locator, 'id', RJS_ID); // pour le path (chemin), on doit préciser l'ID de la requête RJS
			// et le locator est celui par défaut
			locator = RJS_LOCATOR;
		}
		RJS.simpleInvoke(locator, encodeUrl(RJS.URL_ERA_ROOT + path), {
			onSuccess: function(r) {
				chkAndUpd(r && r.sessionId, r);
				chkAndUpdesvId(r && r.profilId);
				if (!callback) {
					LOG.info('ev.requestManager_Pas de callback défini pour ' + path);
				}
				else if (typeof(callback) === 'function') {
					callback(r);
				}
				else if (typeof(callback.onSuccess) === 'function') {
					callback.onSuccess(r);
				}
				else {
					LOG.error("ev.requestManager#invokeEra: Le callback doit être une function ou un objet contenant la propriété 'onSuccess'");
				}
			},
			onError: callback && callback.onError
		},
		force);
	}

	/**
	 * Récupère la session sur l'ERA.<br>
	 * Elle prend en paramètre un objet contenant :<br>
	 * - listener : fonction à appeler lorsque la requête est terminée ou en cas d'échec.
	 * - parentController : fonction appelante.
	 *
	 * @param {{listener: Function, parentController: Function}} _param paramètre multiple
	 */

	getSession = function(_param) {
		// 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é.
		 * @param {Object} r resultat de la dernière requête.
		 */
		function onSuccess(r) {
			_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
		 * @param {Object} r resultat de la dernière requête.
		 */
		function onError(r) {
			_param.listener.call(_param.parentController, {exception: 'MAX_RETRY_REACHED'});
		}

		invokeEraX(PATH_ERA_SESSION, {onSuccess: onSuccess, onError: onError});
	};

	/**
	 * Récupère le profil sur l'ERA.<br>
	 * Elle prend en paramètre un objet contenant :<br>
	 * - listener : fonction à appeler lorsque la requête est terminée ou en cas d'échec.
	 * - parentController : fonction appelante.
	 *
	 * @param {{listener: Function, parentController: Function}} _param paramètre multiple
	 */
	function getProfil(_param) {
		// 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é.
		 * @param {Object} r resultat de la dernière requête.
		 */
		function onSuccess(r) {
			_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
		 * @param {Object} r resultat de la dernière requête.
		 */
		function onError(r) {
			_param.listener.call(_param.parentController, {exception: 'MAX_RETRY_REACHED'});
		}

		// Check si un ID de profil est donnée dans l'ancre de l'URL de la page.
		esvId = ev.tools.getAnchorParameters && ev.tools.getAnchorParameters(window.location.href)[esvIdName];

		// Si pas d'id de profil dans l'ancre de l'URL, on check dans les parametres de l'URL.
		if (!esvId && ev.tools.getParameter) {
			esvId = ev.tools.getParameter(esvIdName);
		}
		// Si pas d'ID de profil dans URL, on check dans les cookies si ceux ci sont activés.
		if (!esvId && ev.tools.isCookieEnabled()) {
			esvId = regexCookieProfil.test(document.cookie) && RegExp.$1;
		}
		var url = PATH_ERA_TRACKING;
		// si le site ID est défini on met à jour les paramètres de la requete de tracking.
		if (window.SITE) {
			url = url + '?siteId=' + window.SITE;
		}else {// Pas de siteID on prend le site par défaut.
			url = url + '?siteId=250';
		}

		// Test si code client ( paramèter clientId ) est dnas l'URL.
		clientId = ev.tools.getParameter('clientId');

		// Pas de code client dans l'URL, on test dans les cookies.
		if (!clientId && ev.tools.isCookieEnabled()) {
			// si le cookie de session est set, pas besoin de request le server.
			clientId = regexCookieCodeClient.test(document.cookie) && RegExp.$1;
			//return;
		}

		if (clientId) { // Attention le code Client est stocké dans l'attribut clientId des URL !!!.
			url = url + '&codeClient=' + clientId;
		}else {
			clientId = '1';
			url = url + '&codeClient=1';
		}

		invokeEraX(url, {onSuccess: onSuccess, onError: onError});
	}

	/**
	 * fonction appelée lorsque la requête de session est terminée.
	 * onsuccess ou onError.
	 * @param {Object} r resultat de la dernière requête.
	 */
	function onProfilRestored(r) {
		if (r.exception) {
			return;
		}

		chkAndUpd(r.sessionId, r);
		chkAndUpdesvId(r.profilId);
		createCodeClientCookie();
		//saveProfil
		/*
		ev.requestManager.invokeEra = function(locator, path, callback, force) {
			invokeEraX(locator, path, callback, force);
		};
		ev.requestManager.invokeEraNoX = function(locator, path, callback, force) {
			invokeEraNoX(locator, path, callback, force);
		};
		makeQueued();
		 */
	}

	/**
	 *  Lecture / création du profil d'identification.
	 */
	function restoreProfil() {
		// Check dans l'ancre.
		esvId = ev.tools.getAnchorParameters && ev.tools.getAnchorParameters(window.location.href)[esvIdName];
		// Check dans les paramètres.
		if (!esvId && ev.tools.getParameter) {
			esvId = ev.tools.getParameter(esvIdName);
		}
		// Check dans les cookies.
		if (!esvId && ev.tools.isCookieEnabled()) {
			esvId = regexCookieProfil.test(document.cookie) && RegExp.$1;
		}
		if (esvId) {
			saveProfil();
		}
		getProfil({
			parentController: window,
			listener: onProfilRestored
		});
	}

	/**
	 * fonction appelée lorsque la requête de session est terminée.
	 * onsuccess ou onError.
	 *
	 * @param {Object} r resultat de la dernière requête.
	 */
	onSessionRestored = function(r) {
		if (r.exception) {
			return;
		}

		LOG.warn('requestMngr#_onSessionRestored(): session restored : ' + r.sessionId);

		// Verif si session est inchangée
		chkAndUpd(r.sessionId, r);

		/**
		 * On a chopé une session valide.
		 * On recherche maintenant un profil valide.
		 */
		//window.esvConfTracking='ON'; // Décommenter pour activer le tracking.
		// systèle de tracking activé.
		if (window.esvConfTracking && window.esvConfTracking === 'ON' && window.SITE) {
			restoreProfil();
		}

		// système de tracking non activé.
		chkAndUpd(r.sessionId, r);

		ev.requestManager.invokeEra = function(locator, path, callback, force) {
			invokeEraX(locator, path, callback, force);
		};

		ev.requestManager.invokeEraNoX = function(locator, path, callback, force) {
			invokeEraNoX(locator, path, callback, force);
		};
		makeQueued();
	};

	/**
	 * Déclaration de la partie visible (publique)
	 */
	ev.requestManager = {
		/**
		 * Permet lire la value du cookie dont le nom est donné en paramètres.
		 * @param {Object} strName Chaine de caractère cotnenant le nom du cookie.
		 */
		getCookieValue: function(strName) {
			return getCookieValue(strName);
		},
		/**
		 * Permet d'encoder l'url passée en paramètre pour
		 * retrouver la session courante dans la page
		 * suivante (ou requête asynchrone).
		 *
		 * @param {!string} 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 {!string} u url à encoder.
		 */
		encodeAnchor: function(u) {
			return encodeAnchor(u);
		},

		/**
		 * 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 #invokeRjs(locator, url, callback, force)
		 *
		 * @param {!(Object|string)} locator [optionel] ev.rjs.Locator spécifique
		 *   à utiliser pour la requête.
		 * @param {?(string|Object|Function)=} path chemin du service demandé
		 *   (avec les paramètres à traiter par le service).
		 * @param {?(Object|Function|boolean)=} 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);
		},

		/**
		 * @see #invokeEraNoX(locator, url, callback, force)
		 *
		 * @param {!(Object|string)} locator [optionel] ev.rjs.Locator spécifique
		 *   à utiliser pour la requête.
		 * @param {?(string|Object|Function)=} path chemin du service demandé
		 *   (avec les paramètres à traiter par le service).
		 * @param {?(Object|Function|boolean)=} 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.
		 */
		invokeEraNoX: function(locator, path, callback, force) {
			//  invokeEraNoX(locator, path, callback, force);
		},
		/**
		 * FIXME ???
		 * @see #invokeEraX(locator, url, callback, force)
		 *
		 * @param {!(Object|string)} locator [optionel] ev.rjs.Locator spécifique
		 *   à utiliser pour la requête.
		 * @param {?(string|Object|Function)=} path chemin du service demandé
		 *   (avec les paramètres à traiter par le service).
		 * @param {?(Object|Function|boolean)=} 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.
		 */
		invokeEraX: function(locator, path, callback, force) {
			invokeEraX(locator, path, callback, force);
		},
		/**
		 * 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 {number} 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) {
			LOG.error('requestManager.retry_deprecated');
			if (!url || typeof(url) !== 'string') {
				LOG.error('Le paramètre url est obligatoire et doit être une url valide (url=' + url + ')');
				return;
			}
			if (!nbError || typeof(nbError) !== 'number') {
				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;
			}
			var locator = new RJS.Locator(5000 * (1 + nbError), name);
			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.
		 * Un parametre time est ajouté dans le src afin de forcer l'envoi sous certain navigateur:ff.
		 * @param {!string} errorMessage message d'erreur à logger sur ERA.
		 */
		errorLogger: function(errorMessage) {
			if (errorMessage) {
				var n = document.createElement('img');
				n.setAttribute('src', RJS.URL_ERA_ROOT + '/erreur.rjs;jsessionid=' + sId + '?erreur=' + encodeURIComponent(errorMessage) + '&time=' + new Date().getTime());
				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.
		 * Un parametre time est ajouté dans le src afin de forcer l'envoi sous certain navigateur:ff.
		 * @param {!string} flagMessage message d'information à logger sur ERA.
		 */
		flagLogger: function(flagMessage) {
			if (flagMessage) {
				var n = document.createElement('img');
				n.setAttribute('src', RJS.URL_ERA_ROOT + '/flag.rjs;jsessionid=' + sId + '?flag=' + encodeURIComponent(flagMessage) + '&time=' + new Date().getTime());
				n = null;
			}
		}
	};

	// première init de la méthode invoke du requestManager
	initInvokeMethod();

	ev.wait(function() {
		// Ici, on est sur que ev.securityManager est créé... ensuite on attend qu'il soit prêt.
		ev.securityManager.wait(function() {
			// Initialisation de tout le système dés que notre ami sec man est prêt
			restoreSession();
		});
	});

	LOG.fatal('requestManager#<init>: ev.requestManager est DEPRECATED => utiliser ev.core.requestManager (+ ev.core.RjsHttpRequest) !');
}());

