package memoire;

import memoire.exception.*;;

/**
 * Classe de gestion de la memoire pour l'execution du programme 
 * @author Richard Ludovic
 * @author Srey Ronald
 * @author Beuque Eric
 */

public class Memoire {
	
	protected int idMEM; // identifiant interne memoire.
	private Pile pile; // pile memoire.
	public Tas tas; // Tas memoire.
	boolean drapeau; //Drapeau de marquage : prend alternativemant la valeur vrai / faux.
	
	
	
	
	/* *** Constructeurs *** */
	
	/**
	 * Constructeur par defaut de la classe memoire.Memoire
	 */
	public Memoire() {
		this.idMEM = 001;
		this.pile = null;
		this.tas = null;
	}
	
	/**
	 * Constructeur de la classe memoire.Memoire.java
	 * @param tailleTas taille du tas a affecter
	 * 
	 */
	public Memoire(int tailleTas) {
		this.idMEM = 001;
		this.pile = new Pile();
		this.tas = new Tas(tailleTas);
	}
	
	/* *** Acces aux membres *** */
	
	/**
	 * Retourne l'identifiant interne de la memoire
	 */
	public int getIdMEM() {
		return idMEM;
	}
	
	/**
	 * Affecte l'identifiant interne de la memoire
	 * @param idMem identifiant a affecter
	 */
	public void setIdMEM(int idMEM) {
		this.idMEM = idMEM;
	}
	
	
	/* *** Axiome de gestion de la pile *** */
	
	/**
	 * Creer une pile
	 * @param q Qua a empiler dan la pile
	 */
	public void creer() throws MemoireException{
		pile = new Pile();
		tas = new Tas(2000);
	}
	
	/**
	 * Empile un Qua dans la pile
	 * @param q Qua a empiler dan la pile
	 */
	public void empiler(Qua q) throws MemoireException{
		if(pile==null){
			throw new PileInexistanteException("Impossible de depiler un Qua car la pile n'existe pas");
		}
		this.pile.empiler(q);
	}
	
	/**
	 * Depile le Qua au sommet de la pile
	 * @return renvoie le Qua depile
	 */
	public Qua depiler() throws MemoireException {
		if(this.estPileVide()){
			throw new PileVideException("Impossible de depiler un Qua car la pile est vide");
		}
		return this.pile.depiler();
	}
	
	/**
	 * Test si une la pile est vide
	 * @return renvoie vrai si la pile est vide, faux sinon
	 */
	public boolean estPileVide() throws MemoireException {
		if(pile==null){
			throw new PileInexistanteException("Impossible de verifier si la pile est vide car la pile n'existe pas");
		}
		return this.pile.estVide();
	}
	
	/**
	 * Ajout des declaration d'une variable.
	 * @param i identifiant
	 * @param v valeur
	 * @param t type
	 */
	public void declVar(String i, Object v, String t) throws MemoireException {
		this.empiler(new Qua(i, v, "var", t));
	}
	
	/**
	 * Effectue l'operation identVal sur la memoire
	 * @param i identifiant
	 * @param t type
	 * @param s pointeur sur la pile
	 */
	public void identVal(String i, String t, int s) throws MemoireException {
		if(!this.estPileVide()){
			Qua q = this.depiler();
			if(s==0){
				this.empiler(new Qua(i,q.val,"var",t));
			}else{
				this.identVal(i, t, s-1);
				this.empiler(q);
			}
		}
	}
	
	/**
	 * Effectue l'operation declCst sur la memoire
	 * @param i identifiant
	 * @param v valeur
	 * @param t type
	 */
	public void declCst(String i, Object v, String t) throws MemoireException {
		if(v==null){
			this.empiler(new Qua(i,v,"vcst",t));// constante non initialisee.
		}else{
			this.empiler(new Qua(i,v,"cst",t));// constante initialisee.
		}
	}
	
	/**
	 * Ajout des declaration d'un tableau.
	 * @param i identifiant
	 * @param v valeur
	 * @param t type
	 */
	public void declTab(String i, Object v, String t) throws MemoireException {
		this.empiler(new Qua(i,this.tas.ajoutTas(i, ((Integer)v).intValue(), t),"tab",t));
	}
	
	/**
	 * Effectue l'operation declMeth sur la memoire
	 * @param i identifiant
	 * @param v valeur
	 * @param t type
	 */
	public void declMeth(String i, Object v, String t) throws MemoireException {
		this.empiler(new Qua(i,v,"meth",t));
	}
	
	/* *** Operations de modification de la memoire *** */
	
	/**
	 * Effectue l'operation affecterVal sur la memoire
	 * @param i identifiant
	 * @param v valeur
	 * @param t type
	 */
	public void affecterVal(String i, Object v) throws MemoireException {
		Qua q = this.depiler();
		if(!i.equals(q.id)){
			this.affecterVal(i, v);
			this.empiler(q);
		}else{
			if(q.obj.equals("vcst")){
				this.empiler(new Qua(q.id,v,"cst",q.sorte));
			}else{
				if(!q.obj.equals("cst")){
					this.empiler(new Qua(i,v,q.obj,q.sorte));
				}
			}
		}
		
	}
	
	/**
	 * Affecte une valeur a une cellule d'un tableau en memoire
	 *
	 * @param id identifiant
	 * @param ind indice dans le tableau
	 * @param val valeur
	 * @return Vrai si l'affectation reussie, faux dans le cas ou le tableau n'est pas trouve.
	 */
	public boolean affecterValT(String id, int ind, Object val) throws MemoireException {
		 
		int adrPile;
		
		if((adrPile = this.pile.rechercher(id)) != -1){// l'identifiant existe : 
			  
			Qua q = pile.elementAt(adrPile);
			
			if(q.obj == "tab"){ // c le bon 
				
				  int adrTas = Integer.parseInt(q.getVal().toString());
				  int i = 0;
				  boolean trouve = false;
					  
				  do{
					  if(i == ind){
						  tas.contenu[adrTas].setValeur(Integer.parseInt(val.toString()));
						  trouve = true;
					  }
					  adrTas = tas.contenu[adrTas].getPsuivant();
					  i++;
				  }while(!trouve && tas.contenu[adrTas].getPsuivant() != -1); 
				  
				  if(trouve){
					  return true;
				  }else if(tas.contenu[adrTas].getPsuivant() == -1 && ind == i){// derniere case
					  tas.contenu[adrTas].setValeur(Integer.parseInt(val.toString()));
					  return true;
				  }
			  }  
		  }
		  return false; // echec 
	}
	
	/**
	 * Affecte un type a un quadruplet en memoire
	 *
	 * @param id identifiant
	 * @param t type
	 * @return Vrai si l'affectation reussie, faux dans le cas ou le quadruplet n'est pas trouve.
	 */
	public boolean affecterType(String id, String t) throws MemoireException {
	 
		int adr;
		if((adr=pile.rechercher(id)) != -1){// l'identifiant existe : 
			this.pile.elementAt(adr).setSorte(t);
			return true;  
		}  
		return false;
	}
	
	/* *** Operations d'acces aux informations de la memoire *** */
	
	/**
	 * Effectue l'operation val sur la memoire
	 * @param i identifiant
	 * @param v valeur
	 * @param t type
	 */
	public Object val(String i) throws MemoireException {
		if(this.estPileVide()){
			return new PileVideException("Impossible de trouver la valeur de " + i + " car la pile est vide");
		}else{
			Qua q = this.depiler();
			if(i.equals(q.id)){
				this.empiler(q);
				return q.val;
			}else{
				Object tmp = this.val(i);
				this.empiler(q);
				return tmp;
			}
		}
	}

	/**
	 * Effectue l'operation val sur la memoire
	 * @param i identifiant
	 * @param v valeur
	 * @param t type
	 */
	public Object valT(String i, int ind) throws MemoireException {
		if(this.estPileVide()){
			return new PileVideException("Impossible de trouver la valeur de " + i + "[" + ind + "] car la pile est vide");
		}else{
			Qua q = this.depiler();
			if(i.equals(q.id)){
				
				this.empiler(q);
				return q.val; // tas.valeurTas(q.val,v)
			}else{
				Object tmp = this.valT(i,ind);
				this.empiler(q);
				return tmp;
			}
		}
	}

	/**
	 * Renvoi une chaine de caractere montrant l'etat de la memoire
	 * @return Chaine de caractere montrant l'etat de la memoire
	 */
	public String toString(){
		String buf = "";
		
		buf += "   > Pile : " + this.pile.toString();
		//buf += "\n";
		//buf += "   > Tas : \n";
		//buf += tas.toString();
		
		return buf;
	} 
	  
//	
//	/* *** Garbage Collector : *** */
//	
//	/**
//	 * Algorithme de Marquage-balayage :
//	 * 	- A chaque bloc alloue  est associe un marqueur (ou drapeau, ou mark flag).
//	 * 	- Si le bloc fait partie d'un objet refference (pointe) par la Pile, on le
//	 * 	  marque (vivant).
//	 * 	- Enfin, on balaye le tas en d�salouant les blocs non marque (les morts).
//	 * @author Richard Ludovic 
//	 */
//
//	private void marquage_balayage(){
//		
//		int i,j, adr, decal;
//		
//		// inversion du drapeau :
//		this.drapeau = ! this.drapeau;
//		
//		// --- Marquage :
//		
//		// parcours de la Pile (racine) :
//		for(i=this.pile.getBp();i<this.pile.getSp();i++){
//			// si on trouve une declaration de tableau :
//			if(pile.contenu.get(i).obj == "tab"){
//				// marquage des vivants dans le tas :
//				adr = Integer.parseInt(pile.contenu.get(i).val.toString());
//				decal = this.tas.contenu[(adr-1)].valeur;
//				for(j=adr-1; j<=adr+decal; j++){
//					this.tas.contenu[j].setMarkflag(this.drapeau);
//				}
//			}
//		}
//		
//		// --- balayage :
//		
//		// on parcours le tas et on supprime les morts :
//		for(i=0; i<this.tas.contenu.length; i++){
//			if(this.tas.contenu[i].markflag != this.drapeau){
//				this.tas.libererBloc(i);
//			}
//		}
//	}
//
//	/**
//	 * Algorithme de compactage :
//	 * 	- deplacement (copie) des objets vers le debut du tas en remplissant les blocs vides
//	 *  - liberation des blocs recopies
//	 *  - au final tous les blocs vides sont a la fin du tas (plus de fragmentation).
//	 *  @author Richard Ludovic 
//	 */
//	private void compactage(){
//		
//		int i, cptBlcFree;
//		cptBlcFree = 0;
//		
//		for(i=0; i<this.tas.contenu.length; i++){//parcours du tas.
//			if(this.tas.contenu[i] == null){ 
//				cptBlcFree++; // compte les blocs vides avant un objet.
//			}else{
//				if(cptBlcFree != 0 ){// on d�cale :
//					this.tas.contenu[(i - cptBlcFree)].valeur = this.tas.contenu[i].valeur;
//					this.tas.libererBloc(i);
//					cptBlcFree--;
//				}
//			}
//		}	
//	}
//	
//	/**
//	 * Execute le garbage colector sur le tas.
//	 * Lorsque ramasse-miettes se d�clenche:
//	 *	- phase de marquage: trouver les vivants
//	 *	- phase de balayage: recycler les morts
//	 *	- phase de compactage pour rassembler les blocs vides.
//	 *@author Richard Ludovic 
//	 */
//
//	public void ramasseMiettes(){
//		this.marquage_balayage();
//		this.compactage();
//	}

		/* test*/
		public static void main(String[] args) throws MemoireException{
			
			Memoire mem = new Memoire(1000);
			String trace ="";
			
			System.out.println("D�but du test m�moire");
			System.out.println("etat initial de la m�moire :");
			System.out.println(mem.toString());
			
			mem.declCst("CONSTANTE", null, "entier");
			mem.declMeth("f","150", "boolean");
			mem.declVar("x", 10, "entier");
			mem.declVar("y", true,"boolean");
			mem.declTab("tab1", 20, "tab");
			mem.affecterVal("CONSTANTE", 1000);
			mem.affecterType("y", "entier");
			for(int k=0; k<20; k++){
				trace += mem.affecterValT("tab1", k, (k+1));
			}
			
				
			System.out.println("etat final de la m�moire :");
			System.out.println(mem.toString());
			 
			System.out.println("fin du test m�moire !");
			System.out.println("trace :");
			System.out.println(trace);
		}
	

}
