/**
 *
 * Ce mecanisme de templat e javascript est basé sur le mécanisme de micro templating de John Resig
 * cf : http://ejohn.org/blog/javascript-micro-templating/
 * 			http://webdev-fr.com/2008/11/05/le-micro-templating-javascript-de-john-resig-a-la-loupe/
 *
 *
 * Cette classe permet de gérer un mécanisme de template : les templates sont des portions de code html dans lesquelles
 * des variables doivent etre remplacées par des attributs d'objets javascript avant d'être injecté dans la page avec les bonnes valeurs.
 *
 * un temlate est défini dans une balise script et contient des variables ecrites de la façon suivante : [¤variable¤]
 * exemple de template :
 * <script type="template/html" id="template_listeHotels">
 *  <ul id="listeHotels">
 *		<ev:hotel>
 *			<li>
 *				<div class="offerHead">
 *					<span>[¤note¤]</span>
 *					<span>[¤distance¤]</span>
 *					<h3>[¤nom¤]</h3>
 *				</div>
 * 			</li>
 * 		</ev:hotel>
 *  </ul>
 * </script>
 *
 * la balise script possede un attribut id qui permet de retrouver ce template (en effet il peut y avoir plusieurs templates dans une seule page)
 * le template contient aussi une balise redefinie pour le systeme de template commencant par <ev: (dans l'exemple ci-desus : <ev:hotel>)
 * en principe ce tag porte le meme nom que le type d'objet auquel on accedera pour remplacer les variables : pour etre plus claire on remplacera [¤nom¤]
 * par la valeur de l'attribut nom d'un objet hotel.
 *
 * dans la page principale on trouve une balise (generalement un <div>) qui contiendra le morceau de code généré à partir du template :
 * exemple :
 * <div id="ev_listeHotels"></div><!-- conteneur de la liste d'hotels (contiendra le template : template_listeHotels) -->
 *
 * Pour iniitaliser le template, on créé un objet TemplateManager :
 * exemple :
 * templManagerHotelList = new TemplateManager("template_listeHotels", "ev_listeHotels", "hotel");
 *
 * Ensuite, lorsque l'on veut affiché le code générer à partir du template on appele la methode : refreshView() à laquelle on passe en argument la liste
 * d'objets qui vont servir de source pour le remplacement des variables
 * exemple :
 * var dataArray = new Array();
 * dataArray.push(hotel1);
 * dataArray.push(hotel2);
 * dataArray.push(hotel3);
 * templManagerHotelList.refreshView(dataArray);
 * nb : On peut aussi passer une liste d'un seul element
 *
 * Le template manager va alors remplacer [¤nom¤] par la valeur retournée par hotel1.getNom().
 *
 *
 * Il est aussi possible de faire appel a des attributs appartenant a des objets de l'objet principal (dans l'exemple l'objet principal etait un hotel)
 * exemple :
 * <script type="template/html" id="template_listeHotels">
 *  <ul id="listeHotels">
 *		<ev:hotel>
 *			<li>
 *				<div class="offerHead">
 *					<span>[¤note¤]</span>
 *					<span>[¤distance¤]</span>
 *					<h3>[¤nom¤]</h3>
 *						<div>
 *								<ul>
 *									<li>
 *										<ev:hotelPartenaire>
 *											<span>[¤nomPartenaire¤]</span>
 *											<span>[¤prix¤]</span>
 *										</ev:hotelPartenaire>
 *									</li>
 *									<li>
 *										<ev:hotelPartenaire>
 *											<span>[¤nomPartenaire¤]</span>
 *											<span>[¤prix¤]</span>
 *										</ev:hotelPartenaire>
 *									</li>
 *								</ul>
 *						</div>
 *				</div>
 * 			</li>
 * 		</ev:hotel>
 *  </ul>
 * </script>
 *
 * l'initialisation devient alors :
 * 	//creation du manager de template pour la liste de resultats
 *		var listeSousElts = {
 *			"propositionsPartenaires": "hotelPartenaire"
 *		};
 *		templManagerHotelList = new TemplateManager("template_listeHotels", "ev_listeHotels", "hotel", listeSousElts);
 *
 * le templateManager va alors remplacer les varibales situées à l'interieur des sous elements de la façon suivante :
 * le premier [¤nomPartenaire¤] sera remplacé par hotel1.getPropositionsPartenaires(1).getNomPartenaire()
 *
 *
 * @param {Object} _idTemplate : id du template qui va permettre de retrouver le templae dans la page.
 * @param {Object} _idDomNode : id de l'element dans lequel on injectera le template avec les bonnes valeurs.
 * @param {Object} _evTagName : correspond au nom employé dans la balise ev. Exemple pour la balise <ev:hotel>, _evTagName = hotel
 * @param {Object} _nbIterations : permet de preciser si on veut repeter le code compris dans le tag principal plusieur fois
 * @param {Object} _subElementLists : liste qui contient tous les sous elements ev: contenu dans l'element ev: principal (_evTagName)
 */
function TemplateManager(_idTemplate, _idDomNode, _evTagName, _nbIterations, _subElementLists){

	this.template = document.getElementById(_idTemplate).innerHTML; // "template" permet de stocker le template.
	this.domNode = document.getElementById(_idDomNode); // "domNode" permet de recuperer le noeud dom auquel on injectera le template avec les bonnes valeurs.
	this.evTagName = _evTagName; // "evTagName" correspond au nom employé dans la balise ev. Exemple pour la balise <ev:hotel>, _evTagName = hotel
	this.subEvElementsList = []; // "subEvElementsList" est une liste qui contient tous les sous elements ev: contenu dans l'element ev: principal (_evTagName)


	this.templateEvBlocs = []; // tableau qui contient des blocs correspondant au decoupage du template principal
	this.templateCodeEvBlocs = []; // tableau contenant des sous tableau qui correspondent au découpage du code de chaque element du tableau "templateEvBlocs"
	this.dataArray = undefined; // "dataArray" correspond au tableau comportant des objets de même type dont les données serviront à remplir le template.


	if(_nbIterations){ // permet de maintenir le nombre d'iteration si on veut repéter plusieurs fois le bout de code compris dans l'element principal <ev:
		this.nbIterations = _nbIterations;
	}else{
		this.nbIterations = 0;
	}

	/**
 * Expression de substitution à remplacer dans le code HTML
 */
	TemplateManager.VARIABLE_EXPRESSION=/\[¤[a-zA-Z0-9,]+¤\]/;

	// initialisation de la liste qui contiendra tous les sous elements ev: contenu dans l'element ev: principal (_evTagName)
	if(_subElementLists){
		for(var i in _subElementLists){
			if(typeof _subElementLists[i]!=='function'){
				this.subEvElementsList[this.subEvElementsList.length]={
							"evEltName":_subElementLists[i],
							"object":i,
							"nb":0
				};
			}
		}
	}
	this.initTemplate();
}



TemplateManager.prototype={

	initTemplate:function(){
		this.templateEvBlocs = this.cutTemplateInBlocs(this.evTagName, this.nbIterations);
	},

	/**
	 * permet d'afficher le template dans la page avec les bonnes valeurs
	 * @param {Object} _dataArray
	 */
	refreshView:function(_dataArray){
		this.dataArray = _dataArray;
		this.domNode.innerHTML=this.fillTemplate();
	},

	/**
	 * permet d'afficher une partie seulement du template
	 * @param {Object} _indexItem
	 * @param {Object} _object
	 * @param {Object} _printAfterPartOnly
	 */
	refreshOneItemView:function(_indexItem, _object, _printAfterPartOnly){
		this.domNode.innerHTML=this.fillOneItemTemplate(_indexItem, _object, _printAfterPartOnly);
	},

	/**
		Cette methode permet de decouper le template en plusieurs blocs
		on parcourt les template pour le decouper en blocs
		il y a trois sortes de blocs
		    - les blocs correspondant à du texte
		    - les blocs qui contiennent du code compris entre les balises principales <ev:>
		    - les blocs qui contiennent du code compris entre les sous balises <ev:>, elles mêmes comprises dans les balises principales  <ev:>

		    on obtient donc un tableau du type :
		    evParts
			[0]	code = false	subElementCode = false	text = "\n \n <ul>\n <li>\n "
			[1]	code = true	subElementCode = false	text = "<ev:hotel> ..."
			[2]	code = true	subElementCode = true	text = "<ev:hotelPartenaire> ...</ev:hotelPartenaire>"
			[3]	code = true	subElementCode = false	text = "\n"
			[4]	code = true	subElementCode = true	text = "<ev:hotelPartenaire> ...</ev:hotelPartenaire>"
			[5]	code = true	subElementCode = false	text = "... </ev:hotel>"
			[6]	code = false	subElementCode = false	text = "\n <li>\n <li>\n "
			[7]	code = true	subElementCode = false	text = "<ev:hotel> ..."
			[8]	code = true	subElementCode = true	text = "<ev:hotelPartenaire> ...</ev:hotelPartenaire>"
			[9]	code = true	subElementCode = false	text = "\n"
			[10]	code = true	subElementCode = true	text = "<ev:hotelPartenaire> ...</ev:hotelPartenaire>"
			[11]	code = true	subElementCode = false	text = "... </ev:hotel>"
			...
	*/
	cutTemplateInBlocs:function(_evTagName, _nbIterations){
		var template = this.template;
		var blockStart=0;
		var inCode=false;
		var inMainEvElement=false;
		var inSubEvElement=false;
		var inSubElementCode=false;
		var evParts=[];
		var numEvElt = 0;
		var subElementObject= "";
		var numSubElement= -1;
		var inPrintAfter= false;

		var indexStartMainEvElement = 0;// permet de maintenir l'index de la case du tableau qui contient l'ouverture du bloc <ev: . Necessaire s'il faut recopier plusiqeru fois le bout de code contenu dans le tag principal <ev:

		while(blockStart<template.length){
			var blockEnd;

			if(inMainEvElement){
				blockEnd = template.indexOf("</ev:"+_evTagName+">",blockStart)+("</ev:"+_evTagName+">").length;
				inMainEvElement = false;
				inCode = true;

				inSubElementCode = false;
				subElementObject = "";
				numSubElement = -1;

				var index;
				if(inSubEvElement){
					for (var i = 0; i < this.subEvElementsList.length; i++) {
						if (this.subEvElementsList[i].evEltName) {
			  				index = template.indexOf("</ev:"+this.subEvElementsList[i].evEltName+">",blockStart);
								if(index != -1 && index < blockEnd){
									blockEnd = index + ("</ev:"+this.subEvElementsList[i].evEltName+">").length;
									numSubElement = this.subEvElementsList[i].nb;
									++this.subEvElementsList[i].nb;
									inMainEvElement = true;
									inSubEvElement = false;
									inSubElementCode = true;
									subElementObject = this.subEvElementsList[i].object;
								}
						}
					}
				}else{
					for (var j = 0; j < this.subEvElementsList.length; j++) {
						if (this.subEvElementsList[j].evEltName) {
			  				index = template.indexOf("<ev:"+this.subEvElementsList[j].evEltName+">",blockStart);
								if(index != -1 && index < blockEnd){
									blockEnd = index;
									inMainEvElement = true;
									inSubEvElement = true;
								}
						}

						if(blockEnd == template.indexOf("</ev:"+_evTagName+">",blockStart)+("</ev:"+_evTagName+">").length){
							this.subEvElementsList[j].nb = 0;// on remet a zero le nombre de sous-elements.
						}

					}
				}

				if(inPrintAfter){
					index = template.indexOf("</ev:printAfter>",blockStart);
					if(index != -1 && index < blockEnd){
						blockEnd = index + ("</ev:printAfter>").length;
					}
				}else{
					index = template.indexOf("<ev:printAfter>",blockStart);
					if(index != -1 && index < blockEnd){
						blockEnd = index;
						if(inSubEvElement){
							inSubEvElement = false;
						}
					}
				}
			}else{
				if(!indexStartMainEvElement){
					indexStartMainEvElement = evParts.length+1;
				}
				indexEndMainEvElement = evParts.length;

				blockEnd = template.indexOf("<ev:"+_evTagName+">",blockStart);
				inMainEvElement = true;
				inCode = false;
				numEvElt++;
			}

			if(blockEnd==-1){
				blockEnd=template.length;
			}

			evParts[evParts.length]={
							"code":inCode,
							"printAfter":inPrintAfter,
							"subElementCode":inSubElementCode,
							"subElementObject":subElementObject,
							"numEvElt":numEvElt-1,
							"numSubElt":numSubElement,
							"text":template.substring(blockStart,blockEnd)
			};

			if(blockEnd != -1 && blockEnd == template.indexOf("<ev:printAfter>",blockStart)){
				inPrintAfter = true;
			}
			if(blockEnd != -1 && blockEnd == index + ("</ev:printAfter>").length){
				inPrintAfter = false;
			}

			blockStart=blockEnd;
		}



		// si nbIterations > 0 alors on va recopier le code se trouvant dans la balise principale <ev: "nbIterations" fois.
		if(this.nbIterations > 1){
			numEvElt = this.nbIterations;
			var lastElementText = evParts[evParts.length-1].text;//on suvegarde le dernier elt du tableau pour le remttre a la fin
			evParts[evParts.length-1].text = lastElementText.substring(0,lastElementText.indexOf("</ev:"+_evTagName+">",0)) + "</ev:"+_evTagName+">";

			var tabSize = evParts.length;
			for(var it = 2; it <= this.nbIterations; it++){
				for(var p = indexStartMainEvElement; p < tabSize; p++){
					//evParts[evParts.length] = evParts[p];
					//evParts[evParts.length-1].text = evParts[p].text;

					evParts[evParts.length]={
							"code":evParts[p].code,
							"printAfter":evParts[p].printAfter,
							"subElementCode":evParts[p].subElementCode,
							"subElementObject":evParts[p].subElementObject,
							"numEvElt":it-1,
							"numSubElt":evParts[p].numSubElt,
							"text":evParts[p].text.replace(/\[¤ev.numIteration¤\]/g,it)
					};
				}
			}
			evParts[evParts.length-1].text = lastElementText;
			for (var k = indexStartMainEvElement; k < tabSize; k++) {
				evParts[k].text = evParts[k].text.replace(/\[¤ev.numIteration¤\]/g,1);
			}
		}


		return evParts;
	},

	/**
		permet d'alimenter le template avec les bonnes valeurs
	*/
	fillTemplate:function(){
		var withData = "";
		for(var i = 0; i < this.templateEvBlocs.length; i++){
			if(!this.templateEvBlocs[i].printAfter) {
		  	if(this.templateEvBlocs[i].code) {
		  		//if(this.dataArray[this.templateEvBlocs[i].numEvElt] != undefined) {
					if(this.dataArray[this.templateEvBlocs[i].numEvElt]) {
		  			this.templateEvBlocs[i].val = this.fillEvTemplate(this.templateEvBlocs[i].text, this.dataArray[this.templateEvBlocs[i].numEvElt], this.templateEvBlocs[i].subElementObject, this.templateEvBlocs[i].numSubElt);
		  			withData += this.templateEvBlocs[i].val;
		  		}
		  	}
		  	else{
		  		withData += this.templateEvBlocs[i].text;
		  	}
		  }
		}
		return withData;
	},


	fillOneItemTemplate:function(_indexItem, _object, _printAfterPartOnly){
		var withData = "";
		if(_printAfterPartOnly){
			var nbEltAffich = this.dataArray.length;
			if(this.templateEvBlocs.length < nbEltAffich){
				nbEltAffich = this.templateEvBlocs.length;
			}

			for(var i = 0; i < this.templateEvBlocs.length; i++){
				if(this.templateEvBlocs[i].numEvElt >=  nbEltAffich){break;}
				if(this.templateEvBlocs[i].numEvElt == _indexItem) {
		  			this.templateEvBlocs[i].val = this.fillEvTemplate(this.templateEvBlocs[i].text, _object, this.templateEvBlocs[i].subElementObject, this.templateEvBlocs[i].numSubElt);
		  			withData += this.templateEvBlocs[i].val;
		  	}
		  	else{
					if(!this.templateEvBlocs[i].printAfter) {
				  	if(this.templateEvBlocs[i].code) {
				  		withData += this.templateEvBlocs[i].val;
				  	}else{
				  		withData += this.templateEvBlocs[i].text;
				  	}
				  }
		  	}
			}
		}
		return withData;
	},


	fillEvTemplate:function(_template, _object, _subElement, _numSubElement){
		var object = _object;
		var template = _template;
		var blocStart=0;

		while((blocStart = template.search(TemplateManager.VARIABLE_EXPRESSION)) != -1){

			var blocEnd = template.indexOf("¤]", blocStart);
			var property = template.substring(blocStart+2,blocEnd);

			var tabParam = property.split(",");
			var parametres = "";
			for (var i=1; i<tabParam.length; i++) {
			 parametres += tabParam[i];
			 if(i!=tabParam.length-1){
			 	 parametres += ",";
			 }
			}

			property = tabParam[0];

			var getter = "get"+  property.charAt(0).toUpperCase() + property.substring(1) + "(" + parametres + ")";

			if(_subElement){ // si le test passe la chaine est forcement non nulle et non vide
				var getterSubElt = "get"+  _subElement.charAt(0).toUpperCase() + _subElement.substring(1) + "(" + _numSubElement + ")";
				var valeurEval=eval("if((object."+getterSubElt+")!=undefined && (object."+getterSubElt+")."+getter+"!=null){(object."+getterSubElt+")."+getter+"}else{'NO_DATA'}");

				if(valeurEval == "NO_DATA"){
					return"";// on sort ici pour ne pas affiché le bloc coorespondant à un sous element ev:
				}else{
					template = template.replace(TemplateManager.VARIABLE_EXPRESSION, valeurEval);
				}
			}else{
				template = template.replace(TemplateManager.VARIABLE_EXPRESSION, eval("(object."+getter+")"));
			}
		}

		return template;
	}
};

