/** file: ExpVisit.java  - VISITOR per le classi delle espressioni "Exp" */

import java.awt.*;
import java.lang.Exception;

/** ===========
      VISITOR 
    ============ */
/** "ExpVisitor" e' l'interfaccia che serve per specializzare
    i "visitor" delle espressioni. 
    Ogni sottoclasse deve implementare tutti i metodi (astratti)
    in modo da eseguire la visita dell'albero che rappresenta
    un'espressione. 
    Ogni metodo si chiama "VisitaXxx" dove "Xxx" e' il nome di
    una classe (non astratta) contenuta nel file "Exp.java" */
interface ExpVisitor {
  public abstract void VisitaPiuExp      (PiuExp e);
  public abstract void VisitaMenExp      (MenExp e);
  public abstract void VisitaPerExp      (PerExp e);
  public abstract void VisitaDivExp      (DivExp e);
  public abstract void VisitaSqrExp      (SqrExp e);
  public abstract void VisitaSqrtExp     (SqrtExp e);
  public abstract void VisitaInvExp      (InvExp e);
  public abstract void VisitaNumExp      (NumExp e);
  public abstract void VisitaAssignExp   (AssignExp e);
  public abstract void VisitaIdentExp    (IdentExp e);
  public abstract void VisitaIdentRefExp (IdentRefExp e) throws Exception;
}//ExpVisitor

/** ==================================
      VISITOR PER DISEGNARE L'ALBERO
    ================================== */
class VisitorDisegnaAlbero implements ExpVisitor {
  Graphics MyGraph;
  TextArea MsgOut;
  java.util.Stack VisStack = new java.util.Stack ();  // lo STACK!
  int level = 0;
  int posx, posy,
      fontSize,
      dd1, dd2;
  int amp = 3;
  int maxx, maxy;
  Integer ingombro = new Integer (0);

  /** Costruisce un "visitor" di visualizzazione che usa "g" come
      oggetto grafico (per disegnare) e il punto di coordinate
      (x,y) dalla posizione alto-sinistra.
      La TextArea serve se si vuole stampare i messaggi di
      costruzione dell'albero grafico) */
  public VisitorDisegnaAlbero (Graphics g, int x, int y,
                               TextArea a) {
    MyGraph=g;
    posx=x;
    posy=y;
    maxx=200;
    maxy=150;
    MsgOut=a;
    MyGraph.setColor (Color.orange);
    MyGraph.fillRect (0,0,600,400);
    MyGraph.setFont (new Font("Curier", Font.BOLD, 18));
    fontSize = MyGraph.getFont().getSize() -5;
    dd1 = fontSize;
    dd2 = dd1 / 2;
  }

  /** Disegna un nodo rotondo con uno specifico colore di fondo,
      luce e ombra, a una certa posizione e con un testo */
  protected void DisegnaNodo (Color cfill, Color cluc, Color comb,
                              int posNodeX, int posNodeY,
                              String Testo) {
    int i;
    int max = 2;
    int tmp;
    MyGraph.setColor (cfill);
    MyGraph.fillOval (posNodeX-dd1, posNodeY-dd1-dd2,
                      dd1*3+max, dd1*2+dd2+max);
    MyGraph.setColor (cluc);
    for (i=0; i<=max; i++)
      MyGraph.drawArc (posNodeX-dd1+i, posNodeY-dd1-dd2+i,
                       dd1*3, dd1*2+dd2,
                       50, 130);
    MyGraph.setColor (comb);
    for (i=0; i<=max; i++)
      MyGraph.drawArc (posNodeX-dd1, posNodeY-dd1-dd2,
                       dd1*3+i, dd1*2+dd2+i,
                       230, 130);
    MyGraph.setColor(Color.white); 
    MyGraph.drawString(Testo, posNodeX-3-1,posNodeY+3-1);
    MyGraph.setColor(Color.black);   
    MyGraph.drawString(Testo, posNodeX-3+1,posNodeY+3+1);
    MyGraph.setColor(Color.blue);
    MyGraph.drawString(Testo, posNodeX-3,  posNodeY+3);
    tmp=(posNodeX-dd1)+(dd1*3+max);
    if (maxx < tmp) maxx=tmp;
    tmp=(posNodeY-dd1-dd2)+(dd1*2+dd2+max);
    if (maxy < tmp) maxy=tmp;
  }//DisegnaNodo
    
  /** Rende la massima posizione "x" a cui arriva il disegno */
  public int RendiMaxX() { return maxx; }
  /** Rende la massima posizione "y" a cui arriva il disegno */
  public int RendiMaxY() { return maxy; }

  /** Traccia l'albero grafico dall'operatore "op" scendendo 
      ricorsivamente nel sottoalbero di sinistra e di destra */
  protected void DisegnaAlbero (Exp sonl, Exp sonr, String op) {
    int level = VisStack.size();
    int oldIngombroVal,ingombroVal,delta;
    int posNodeY, posNodeX,
        posSonSinX,posSonDesX, posSonY;
    VisStack.push(ingombro);
    try {
      sonl.accept (this);
      posSonSinX = ((Integer)VisStack.pop()).intValue();
      sonr.accept (this);
      posSonDesX = ((Integer)VisStack.pop()).intValue();
      oldIngombroVal= ((Integer)VisStack.pop()).intValue();
      ingombroVal   = ingombro.intValue();
      delta         = ingombroVal - oldIngombroVal;
      posNodeY      = posy + level*fontSize*amp;
      posNodeX      = posx + oldIngombroVal+delta/2-fontSize/2;
      posSonY       = posNodeY + fontSize * amp ;
      MyGraph.setColor (Color.black);
      MyGraph.drawLine (posNodeX-dd2,   posNodeY,
                        posSonSinX+dd2, posSonY-dd1);
      MyGraph.drawLine (posNodeX+dd2,   posNodeY+fontSize/2,
                        posSonDesX+dd2, posSonY-dd1);
      DisegnaNodo (Color.red, Color.white, Color.gray,  //OPERAZIONE
                   posNodeX, posNodeY, op);
      VisStack.push (new Integer(posNodeX));
    } catch (Exception e)
          { MsgOut.appendText ("!!! ERRORE !!!   "+ e +"\n"); }
  }//DisegnaAlbero
  protected void DisegnaAlberoMono (Exp sah, String op) {
    int level = VisStack.size();
    int oldIngombroVal,ingombroVal,delta;
    int posNodeY, posNodeX,
        posSonAheadX, posSonY;
    VisStack.push(ingombro);
    try {
      sah.accept (this);
      posSonAheadX = ((Integer)VisStack.pop()).intValue();
      oldIngombroVal= ((Integer)VisStack.pop()).intValue();
      ingombroVal   = ingombro.intValue();
      delta         = ingombroVal - oldIngombroVal;
      posNodeY      = posy + level*fontSize*amp;
      posNodeX      = posx + oldIngombroVal+delta/2-fontSize/2;
      posSonY       = posNodeY + fontSize * amp ;
      MyGraph.setColor (Color.black);
      MyGraph.drawLine (posNodeX-dd2,   posNodeY,
                        posSonAheadX+dd2, posSonY-dd1);
      DisegnaNodo (Color.red, Color.white, Color.gray,  //OPERAZIONE
                   posNodeX, posNodeY, op);
      VisStack.push (new Integer(posNodeX));
    } catch (Exception e)
          { MsgOut.appendText ("!!! ERRORE !!!   "+ e +"\n"); }
  }//DisegnaAlberoMono
    
  /** Visita il nodo, istanza di una sottoclasse di "OpExp" */
  protected void VisitaOpExp (OpExp e) {
    DisegnaAlbero (e.left(), e.right(), e.op());
  }
  
  protected void VisitaUnExp (UnExp e) {
    DisegnaAlberoMono (e.ahead(), e.op());
  }

  /** Visita una foglia dell'albero */
  protected void VisitaAtomicExp (Exp e) {
    int level    = VisStack.size();
    int posNodeX = posx+ingombro.intValue();
    int posNodeY = posy+level*fontSize*amp;
    DisegnaNodo (Color.green, Color.yellow, Color.lightGray,   //NUMERO
                 posNodeX,posNodeY, e.toString()); 
    ingombro = new Integer(ingombro.intValue()+fontSize*amp);
    VisStack.push (new Integer(posNodeX));
  }//VisitaAtomicExp

  public void VisitaPiuExp (PiuExp e) { VisitaOpExp(e); }
  public void VisitaMenExp (MenExp e) { VisitaOpExp(e); }
  public void VisitaPerExp (PerExp e) { VisitaOpExp(e); }
  public void VisitaDivExp (DivExp e) { VisitaOpExp(e); }
  public void VisitaSqrExp (SqrExp e) { VisitaUnExp(e); }
  public void VisitaSqrtExp(SqrtExp e){ VisitaUnExp(e); }
  public void VisitaInvExp (InvExp e) { VisitaUnExp(e); }
  public void VisitaNumExp (NumExp e) { VisitaAtomicExp (e); }
  public void VisitaIdentExp (IdentExp e)       {VisitaAtomicExp(e);}
  public void VisitaIdentRefExp (IdentRefExp e) {VisitaAtomicExp(e);}
  public void VisitaAssignExp (AssignExp e)     { VisitaOpExp (e);  }
}//VisitorDisegnaAlbero

/** ========================================
      VISITOR PER RISCRIVERE L'ESPRESSIONE
    ======================================== */
class VisitorRiscriviExp implements ExpVisitor {
  TextArea MsgOut;
  String Valore = "";
  int Notazione;  // 0=PRE, 1=IN,  2=POST !!!
  java.util.Stack VisStack = new java.util.Stack();  // lo STACK

  public VisitorRiscriviExp (int InCheNotazione, TextArea a) {
    MsgOut = a;
    Notazione = InCheNotazione; // 0=PRE, 1=IN,  2=POST !!!
  }
  public VisitorRiscriviExp (TextArea a) {
    this (1, a);  // default: notazione infissa
  }

  public String Val() {
    if (VisStack.empty())
      MsgOut.appendText ("   STACK VUOTO! Valore Vecchio = "+ Valore +"\n");
    else {
      Valore = (String) VisStack.pop();  //ESTRAE!
      MsgOut.appendText ("   POP da stack str.= '"+ Valore +"'\n");
    }
    return Valore;  //se stack vuoto, rende il vecchio 'Valore'
  }//Val

  protected void VisitaOpExp (OpExp e) {
    String StrSin, StrDes, StrParz;
    try {
      e.left().accept(this);          // Visita il sottoalbero sinistro
      StrSin=(String) VisStack.pop(); // Salva il risultato parziale
      e.right().accept(this);         // Visita il sottoalbero destro
      StrDes=(String) VisStack.pop(); // Salva il risultato parziale
      switch (Notazione) {            // Crea la stringa parziale
        case 0: // PREfissa
                StrParz="( "+e.op()+" "+StrSin+" "+StrDes+" )";
                break;
        case 1: // INfissa
                StrParz="( "+StrSin+" "+e.op()+" "+StrDes+" )";
                break;
        case 2: // POSTfissa
                StrParz="( "+StrSin+" "+StrDes+" "+e.op()+" )";
                break;
       default: throw new Exception("Notazione Non Esistente");
      }
      VisStack.push (StrParz);     // Inserisce in stack la stringa
      MsgOut.appendText ("    PUSH str.parziale '"+ StrParz +"'\n");
    } catch (Exception ecc)
          { MsgOut.appendText ("!!! ERRORE !!!   "+ ecc +"\n"); }
  }//VisitaOpExp
  
  protected void VisitaUnExp (UnExp e) {
    String StrAva, StrParz;
    try {
      e.ahead().accept(this);         // Visita l'unico sottoalbero
      StrAva=(String) VisStack.pop(); // Salva il risultato parziale
      switch (Notazione) {            // Crea la stringa parziale
        case 0: // PREfissa
        case 1: // INfissa
                StrParz="( "+e.op()+" "+StrAva+" )";
                break;
        case 2: // POSTfissa
                StrParz="( "+StrAva+" "+e.op()+" )";
                break;
       default: throw new Exception("Notazione Non Esistente");
      }
      VisStack.push (StrParz);     // Inserisce in stack la stringa
      MsgOut.appendText ("    PUSH str.parziale '"+ StrParz +"'\n");
    } catch (Exception ecc)
          { MsgOut.appendText ("!!! ERRORE !!!   "+ ecc +"\n"); }
  }//VisitaUnExp

  public void VisitaPiuExp     (PiuExp e) { VisitaOpExp(e); }
  public void VisitaMenExp     (MenExp e) { VisitaOpExp(e); }
  public void VisitaPerExp     (PerExp e) { VisitaOpExp(e); }
  public void VisitaDivExp     (DivExp e) { VisitaOpExp(e); }
  public void VisitaSqrExp     (SqrExp e) { VisitaUnExp(e); }
  public void VisitaSqrtExp    (SqrtExp e){ VisitaUnExp(e); }
  public void VisitaInvExp     (InvExp e) { VisitaUnExp(e); }
  public void VisitaNumExp     (NumExp e) { VisStack.push(e.toString()); }
  public void VisitaIdentExp   (IdentExp e)    { VisStack.push(e.Val()); }
  public void VisitaIdentRefExp(IdentRefExp e) { VisStack.push(e.Val()); }
  public void VisitaAssignExp  (AssignExp e)   { VisitaOpExp (e); }
} //VisitorRiscriviExp


/** =======================================
      VISITOR PER VALUTARE UN ESPRESSIONE
    ======================================= */
class VisitorValutaExp implements ExpVisitor {
  TextArea MsgOut;
  double Valore;      // Valore calcolato dell'espressione
  double Arg1, Arg2;  // Valori dei due argomenti correnti

  java.util.Stack VisStack = new java.util.Stack();  //lo stack!

  java.util.Hashtable Ambiente; // tavola per l'ambiente globale!


  public VisitorValutaExp (TextArea a) throws Exception {
    // Valuta Exp in ambiente vuoto
    MsgOut = a;
    Ambiente = new java.util.Hashtable(); // ALLOCO NUOVO AMBIENTE!
  }  
  public VisitorValutaExp (java.util.Hashtable AmbAttuale,
                           TextArea a) throws Exception {
    // Valuta Exp nell'ambiente passato!
    this (a); // chiama costruttore di sopra
    Ambiente = AmbAttuale;   // ALIASING: CONDIVIDO LA STRUTTURA!
  }
  
  protected void VisitaOpExp (OpExp e) {
    MsgOut.appendText (" Operazione (nodo)="+ e.myOp() +"\n");
    try {
      // Valuta sottoalbero sinistro
      e.left().accept(this);
      // e poi quello destro
      e.right().accept(this);
      //Estrae valore di destra
      Arg1 = ((Double)VisStack.pop()).doubleValue();
      MsgOut.appendText ("  Pop  I arg. (destra)  = "+ Arg1 +"\n");
      //Estrae valore di sinistra
      Arg2 = ((Double)VisStack.pop()).doubleValue();
      MsgOut.appendText ("  Pop II arg.(sinistra) = "+ Arg2 +"\n");
    } catch (Exception ecc)
          { MsgOut.appendText ("!!! ERRORE !!!   "+ ecc +"\n"); }
  }//VisitaOpExp
  
  protected void VisitaUnExp (UnExp e) {
    MsgOut.appendText (" Operazione (nodo)="+ e.myOp() +"\n");
    try {
      // Valuta sottoalbero
      e.ahead().accept(this);
      //Estrae valore
      Arg1 = ((Double)VisStack.pop()).doubleValue();
      MsgOut.appendText ("  Pop    arg. (unico)   = "+ Arg1 +"\n");
    } catch (Exception ecc)
          { MsgOut.appendText ("!!! ERRORE !!!   "+ ecc +"\n"); }
  }//VisitaUnExp

  public double Val() {
    if (VisStack.empty())
      MsgOut.appendText ("   Stack Vuoto! Valore Vecchio = "+ Valore +"\n");
    else {
      Valore=((Double)VisStack.pop()).doubleValue();
      MsgOut.appendText ("  POP da stack = "+ Valore +"\n");
    }
    return Valore;  //se stack vuoto, rende il vecchio 'Valore'
  }//Val
  
  protected void EseguiPush (double ValOper, String OpCod) {
    MsgOut.appendText (" Push di "+ValOper+" (="+Arg1+OpCod+Arg2+")\n");
    VisStack.push (new Double(ValOper));
  }//EseguiPush
  
  protected void EseguiPushMono (double ValOper, String OpCod) {
    MsgOut.appendText (" Push di "+ValOper+" (="+OpCod+Arg1+")\n");
    VisStack.push (new Double(ValOper));
  }//EseguiPush

  public void VisitaPiuExp (PiuExp e) {
    VisitaOpExp (e);
    EseguiPush (Arg2+Arg1, "+");      //ESEGUE L'OPERAZIONE!
  }
  public void VisitaMenExp (MenExp e) {
    VisitaOpExp(e);
    EseguiPush (Arg2-Arg1, "-");
  }
  public void VisitaPerExp (PerExp e) {
    VisitaOpExp (e);
    EseguiPush (Arg2*Arg1, "*");
  }
  public void VisitaDivExp (DivExp e) {
    VisitaOpExp (e);
    EseguiPush (Arg2/Arg1, "/");
  }
  public void VisitaSqrExp (SqrExp e) {
    VisitaUnExp(e);
    EseguiPushMono (Arg1*Arg1, "sqr");
  }
  public void VisitaSqrtExp(SqrtExp e){
    VisitaUnExp(e);
    EseguiPushMono (Math.sqrt(Arg1), "sqrt");
  }
  public void VisitaInvExp (InvExp e) {
    VisitaUnExp(e);
    if (Arg1 != 0)
      EseguiPushMono (1/Arg1, "inv");
    else
      EseguiPushMono (Double.POSITIVE_INFINITY, "inv");
  }
  public void VisitaNumExp (NumExp e) {
    MsgOut.appendText (" Contenuto(foglia)="+ e +"\n");
    MsgOut.appendText (" Push di "+e+"\n");
    VisStack.push (new Double(e.Val()));
  }

  public void VisitaAssignExp (AssignExp e) {
    String  Arg1;  //identificatore
    Double  Arg2;
    try {
      e.left().accept(this);  // Visita l'IDENTIFICATORE
      Arg1 = (String)VisStack.pop();
      MsgOut.appendText ("  Pop variab.  da assegnare: "+ Arg1 +"\n");
      e.right().accept(this);  // Visita (valuta) l'ESPRESSIONE
      Arg2 = (Double)VisStack.pop();
      MsgOut.appendText ("  Pop espress. da assegnare: "+ Arg2.toString() +"\n");
      Ambiente.put (Arg1, Arg2);  // SIDE-EFFECT: MODIFICA L'AMBIENTE
      MsgOut.appendText (" Assegnazione (in ambiente) "+ Arg1 +" / "+ Arg2 +"\n");
      MsgOut.appendText (" Push di "+Arg2+"\n");
      VisStack.push (Arg2);  // rende il valore calcolato nell'assegnazione
    } catch (Exception ecc)
          { MsgOut.appendText ("!!! ERRORE !!!   "+ ecc +"\n"); }
  }
  
  public void VisitaIdentExp (IdentExp e) {
    String Arg = e.Val(); // Salva l'identificatore nello stack
    MsgOut.appendText (" Push di "+Arg+"\n");
    VisStack.push (Arg);
  }

  public void VisitaIdentRefExp (IdentRefExp e) throws Exception {
    // Recupera il valore della variabile da "Ambiente"
    MsgOut.appendText (" Cerco in ambiente 'Ident. "+ e.Val() +"\n");

    for (java.util.Enumeration h=Ambiente.elements(); h.hasMoreElements(); ) {
      MsgOut.appendText (" > "+ h.nextElement() +"\n");
    }
    Double Arg = (Double) Ambiente.get(e.Val());   //e.Val().intern()
    if (Arg == null) {
      VisStack.push (new Integer(0));
      throw new Exception("ERRORE DI RIFERIMENTO");
    }
    else {
      MsgOut.appendText (" Trovato in ambiente: "+ Arg.doubleValue() +"\n");
      MsgOut.appendText (" Push di "+Arg+"\n");
      VisStack.push (Arg);
    }
  }
}//VisitorValutaExp

//*FINE FILE*