package client;

/** <b>Ein Rechner mit Stack-Architektur (model)</b>. <br />
 *  <br />
 *  Ein Objekt dieser Klasse stellt die reine Statusmaschine (model, Automat)
 *  eines Stackrechners dar. Es gibt vier Stackregistern <strike>--</strike>
 *  x, y, z und t <strike>--</strike> genau wie bei den berühmten
 *  HP-Taschenrechnern.<br />
 *  <br />
 *  Die Anzeige (view) ist nicht enthalten. Sie wird insofern unterstützt,
 *  dass für alle vier Register String-Werte geliefert werden.
 *  Fehlerzustände werden durch 
 *  Nicht-Zahlen-Texte des X-Registers signalisiert.<br />
 *  <br />
 *  Die Java-Nutzung dieser Klasse ist so eingeschränkt, dass eine
 *  problemlose Übersetzung in JavaScript mit dem Google-Webtoolkit (GWT)
 *  möglich ist.<br />
 *  <br />
 *  Dezimale und hexadezimale Darstellung werden unterstützt.<br />
 *  <br />
 *  Die (Tasten-) Eingaben für Ziffern, Enter und Operationen gehören nicht
 *  zum ({@link StackCalcMod}-) Objekt. Sie sind über öffentliche Methoden
 *  ansteuerbar. Damit ist ein beliebig gestalteter Controller 
 *  einsetzbar.<br />
 *  <br /> 
 *  Auf einer <a href="http://a-weinert.de/java/gwt/stack_calc.html"
 *  target="_top">Demo-Seite</a> kann man sich dies alles anschauen.<br />
 *  <br /> 
 *  Copyright 2007 &nbsp; Albrecht Weinert <br /> 
 *  <br />
 *  @see StackCalcGWT
 *
 *  @author   Albrecht Weinert
 *  @version  1 (21.01.2010)
 */
 // Bisher :   V02.00 (30.01.2007 11:44) : neu aus Rechner.java (Applet)
 //            V01.01 (23.09.2007 17:28) : /**
 //            V01.02 (07.10.2009 18:26) : autoEnter Verderben durch History
 //              korrigiert (GWT-Hinw.: History setzen spielt diese zurück!)
 //            V.124+ (07.10.2009 19:00) : nach SVN und weWebSite
 //            V.05+  (10.01.2010 14:48) : to Frame4J (& GWT 2.0) SVN-rev-jmp
    
public class StackCalcMod {
   
   public double[] state() {
      double[] ret =  new double[5];
      ret[0] = x;
      ret[1] = y;
      ret[2] = z;
      ret[3] = t;
      if (autoEnter) ret[4] = 1;
      return ret;     
   } // state() 
   
   public void set(double[] state) {
      x = state[0];
      y = state[1];
      z = state[2];
      t = state[3];
      enterTo0  = true;
      autoEnter = state[4] != 0; // geä. am 07.10.2009 /// false;
      afterDot  = false;
      error     = false;
      errText   = null;
   } // set(double[])

/** Hauptregister X (=Anzeige). <br /> */
   protected double x;

/** Stackregister Y (Stack ist x, y, z, t). <br /> */
   protected double y,z,t;

/** Zustand Hex-Rechner. <br /> */
   protected boolean hex;

   
/** Zustand: nächste Zifferneingabe löscht vorher X. <br /> */
   protected boolean enterTo0 = true;

/** Zustand: nächste Zifferneingabe macht Enter. <br /> */
   protected boolean autoEnter;

  
/** Zustand: in Zifferneingabe nach Dezimalpunkt. <br /> */
   protected boolean afterDot;
   
/** Wert: Dezimalfaktor für nächste einzelne Nachkommastelle. <br /> */ 
   double dotFactor;

   
/** Zustand Fehler. <br /> */
   protected boolean error;

/** Fehlertext. <br /> */
   protected String errText;


/** X-Text liefern. <br /> */
   public String showX() {
      if (error) {
         if (errText == null) errText = "Error";
         return errText;
      }
      if (x == 0 && afterDot) return "0.";
      String ret = formatStackValue(x);
      if (hex) {
         final int le = 28 - ret.length();
         ret = "[hex]                         ".substring(0, le) + ret;
      }
      ///  TEST ret = ret + (autoEnter ? 'a' : '#');
      return ret;
   } // showX()

/** Formatieren eines (Stack-) Werts. <br />
 *  <br />
 *  @return formatierte Zahl
 */
   protected String formatStackValue(double w) {
      if (Double.isNaN(w)) {
         return "Error: NaN (not a number)";
      }
      if (w == 0) return "0";
      if (hex) {
          // JavaScript kann kein Double.toHex
         return Integer.toHexString((int) w);
      }
      String ret = Double.toString(w);
      if (!afterDot || dotFactor != 0.1  || Double.isInfinite(w)) return ret; 
      return ret + '.'; 
   } // formatStackValue(double)


/** Y-Text liefern. <br /> */
   public String showY() {
      if (y == 0) return "0";
      if (hex) {
         return Integer.toHexString((int) y);
      }
      return Double.toString(y);
   } // showY()


/** Z-Text liefern. <br /> */
   public String showZ() {
      if (z == 0) return "0";
      if (hex) {
         return Integer.toHexString((int) z);
      }
      return Double.toString(z);
   } // showY()


/** T-Text liefern. <br /> */
   public String showT() {
      if (t == 0) return "0";
      if (hex) {
         return Integer.toHexString((int) t);
      }
      return Double.toString(t);
   } // showY()


/** Zustand: Fehler. <br /> */
   public final boolean isError() {
      return error || Double.isNaN(x);
   }

/** Wert vom T-Register. <br /> */
   public final double getT() {  return t;  }

/** Wert vom X-Register. <br /> */
   public final double getX() { return x; }

/** Wert vom Y-Register. <br /> */
   public final double getY() { return y; }

/** Wert vom Z-Register. <br /> */
   public final double getZ() { return z;  }


/** Eingabe einer Ziffer (0..9; ..15). <br />
 *  <br />
 *  Diese Methode ist mit entsprechendem Parameter
 *  von den Zifferntasten 0..9,A..F aufzurufen.<br />
 *  <br />
 *  Der x-Wert entsprechend geändert. Nach einer 
 *  vorangegangenen Operation wird vorher 
 *  {@link #enter()} ausgeführt.<br />
 *  <br />
 *  return false, falls Ziffer außerhalb 0..9/15 oder overflow bei dezimal
 */
   public boolean zif(int zif) {
      if (zif < 0 || zif > 15 || zif >= 10 && !hex) return false;
      if (autoEnter) enter();
      final boolean was0 = enterTo0 || x == 0;
      enterTo0 = false;
      if (was0) {
         if (! afterDot) {
            x = zif;
            return true;
         } 
         x = 0; 
      }
      if (hex) {
          x = x * 16 + zif;
          return true; // ignore overflow in hex
      }
      if (afterDot) {
         x += zif * dotFactor;
         dotFactor *= 0.1;
         return true;
      }
      x = x * 10 + zif;
      return true;
   } // zif(int)
   
/** Dezimalkomma-Eingabe. <br />
 *  <br />
 * @return true, wenn vorher noch nicht
 */   
   public boolean dot() {
      if (afterDot) return false;
      if (autoEnter) enter();
      final boolean was0 = enterTo0 || x == 0;
      enterTo0 = false;
      if (was0) x = 0;
      dotFactor = 0.1;
      afterDot = true;
      return true;
   } // dot()

/** Multiplikation x = x * y. <br />
 *  <br />
 *  Diese Methode multipliziert die beiden unteren Stackregister
 *  und &quot;zieht&quot; den Stack eins runter (t wird nach
 *  z kopiert). <br />
 */
   public void mal() {   
      x *= y;
      popToY();
   } // mal()

/** Umrechnungsfaktor rad / grad. <br />
 *  <br />
 *  Wert: {@value}
 */
   public static final double TO_RAD_FAC = Math.PI / 180;
   
/** Wurzel aus 2. <br />
 *  <br />
 *  Wert: {@value}
 */
   public static final double SQRT2 =        //  = Math.sqrt(2.0);
        1.4142135623730950; // 488016887242096980785696718753769480731;
   
   
/** Einstellige Funktion x = f(x). <br />
 *  <br />
 *  Definiert sind: sin, cos, tan, asin, acos atan, sqrt, exp, ln,
 *  toRad und toDeg.<br />
 *  Als &quot;Setzwerte sind definiert: pi, e und sqrt2.<br />
 *  <br />
 *  Aufruf mit einem undefinierten Funktionsnamen ist die 
 *  Einheitsfunktion f(x) = x; der Wert wird bezüglich Zifferneingabe
 *  abgeschlossen (auto-Enter).<br />
 *  <br />
 */
   public void fun(String f) {
      autoEnter = true;
      afterDot = false;
      if (f == null || f.length() == 0) return;
      if ("sqrt".equals(f)) {
         x = Math.sqrt(x);
         return;
      }
      if ("sin".equals(f)) {
         x = Math.sin(x);
         return;
      }
      if ("cos".equals(f)) {
         x = Math.cos(x);
         return;
      }
      if ("tan".equals(f)) {
         x = Math.tan(x);
         return;
      }
      if ("asin".equals(f)) {
         x = Math.asin(x);
         return;
      }
      if ("acos".equals(f)) {
         x = Math.acos(x);
         return;
      }
      if ("atan".equals(f)) {
         x = Math.atan(x);
         return;
      }
      if ("exp".equals(f)) {
         x = Math.exp(x);
         return;
      }
      if ("ln".equals(f)) {
         x = Math.log(x);
         return;
      }
      if ("toRad".equals(f)) {
         x *= TO_RAD_FAC;
         return;
      }
      if ("toDeg".equals(f)) {
         x /= TO_RAD_FAC;
         return;
      }
      if ("pi".equalsIgnoreCase(f)) {
         setX(Math.PI);
         return;
      }
      if ("e".equalsIgnoreCase(f)) {
         setX(Math.E);
         return;
      }
      if ("sqrt2".equalsIgnoreCase(f)) {
         setX(SQRT2);
         return;
      }
      
      /* xxxxx    if ("atan2".equals(f)) {
         x = Math.atan2(y, x);
         y = z;
         z = t;
         return;
      } xx atan2 wird nicht in JavaScript übersetzt, obgl. es gehen müsste */ 

  } // fun(String) 

/** Division (ganzzahlig) x = y / x. <br />
 *  <br />
 *  Diese Methode verknüpft die beiden unteren Stackregister
 *  und &quot;zieht&quot; den Stack eins runter (t wird nach
 *  z kopiert). <br />
 */
   public void teilen() {
      autoEnter = true;
      afterDot = false;
      try {
         x = y / x;
         y = z;
         z = t;
      } catch (Exception e) {
         error = true;
         errText = "Error: " + e.getMessage();
         enterTo0 = true;
      } 
   } // teilen()


/** Division und Rest (ganzzahlig) x,y  = (y / x), (x mod y). <br />
 *  <br />
 *  Diese Methode berechnet den ganzzahligen Quotienten y / x und den
 *  ganzzahligen Rest y mod x (y % x in Java). der neue Inhalt von
 *  x ist der Quotient und der von y der Rest. Durch Vertauschen mit
 *  {@link #chgXY()} kann man beide Ergebnisse abwechselnd in's 
 *  x-Register bringen.<br />
 *  Die Register z und t bleiben unverändert. <br />
 */
   public void teilen2() {
      autoEnter = true;
      afterDot = false;
      try {
         double tempX = y / x;
         double tempY = y % x; // Zwei temps wg. mgl. Except.
         y = tempY;
         x = tempX;
      } catch (Exception e) {
         error = true;
         errText = "Error: " + e.getMessage();
         enterTo0 = true;
      } 
   } // teilen2()


/** Modulo (ganzzahliger Rest) x = y % x. <br />
 *  <br />
 *  Diese Methode verknüpft die beiden unteren Stackregister und 
 *  &quot;zieht&quot; den Stack eins runter (t wird nach z kopiert). <br />
 */
   public void mod() {
      autoEnter = true;
      afterDot = false;
      try {
         x = y % x;
         y = z;
         z = t;
      } catch (Exception e) {
         error = true;
         errText = "Error: " + e.getMessage();
         enterTo0 = true;
      } 
   } // mod()



/** Exponent (y hoch x) x = y ** x. <br />
 *  <br />
 *  Diese Methode verknüpft die beiden unteren Stackregister und 
 *  &quot;zieht&quot; den Stack eins runter (t wird nach z kopiert). <br />
 */
   public void yHx() {
      autoEnter = true;
      afterDot  = false;
      try {
         x = Math.pow(y, x);
         y = z;
         z = t;
      } catch (Exception e) {
         error = true;
         errText = "Error: " + e.getMessage();
         enterTo0 = true;
      } 
   } // yHx() 


/** Addition x = y + x. <br />
 *  <br />
 *  Diese Methode verknüpft die beiden unteren Stackregister und 
 *  &quot;zieht&quot; den Stack eins runter (t wird nach z kopiert). <br />
 */
   public void plus() {
      x += y;
      popToY();
   } // plus()

/** Hilfsmethode. <br />
 *  <br />
 */
   protected void popToY() {
      y = z;
      z = t;
      autoEnter = true;
      afterDot = false;
   } // popToY()


/** Subtraktion x = y - x. <br />
 *  <br />
 *  Diese Methode verknüpft die beiden unteren Stackregister und 
 *  &quot;zieht&quot; den Stack eins runter (t wird nach z kopiert). <br />
 */
   public void minus() {
      x = y - x;
      popToY();
   } // minus()


/** Ändern des Vorzeichens von x:   x = -x. <br />
 *  <br />
 *  Diese Methode ändert das Vorzeichen des Inhalts des Hauptstackregisters
 *  x. Die anderen drei Stackregister bleiben unverändert.
 */
   public void chgSign() {
      x = -x;
      autoEnter = true;
      afterDot = false;
   } // chgSign()

/** Hex-Dez-Umschaltung. <br />
 *  <br />
 *  Diese Methode setzt den Modus Hexadezimal oder Dezimal.<br />
 *  Ein ggf. Änderung wird über die Rückgabe signalisiert und wirkt
 *  wie Eingabeabschluss (nächste Ziffer auto Enter). <br />
 *  <br />
 *  @return Änderung
 */
   public boolean setHex(boolean hex) {
      if (this.hex == hex) return false;
      this.hex = hex;
      autoEnter = true;
      return true;
   } // setHex()


/** Hex-Dez-Umschaltung (toggle). <br />
 *  <br />
 *  Diese Methode ändert den Modus Hexadezimal / Dezimal und wirkt
 *  wie Eingabeabschluss (nächste Ziffer auto Enter). <br />
 *  <br />
 *  @return hex
 */
   public boolean toggleHex() {
      autoEnter = true;
      return hex = !hex;
   } // toggleHex()



/** Hex-Dez-Zustand. <br />
 *  <br />
 *  Diese Methode ändert den Modus Hexadezimal oder 
 *  Dezimal.<br />
 *  <br />
 *  @return Neuer Zustand (hex)
 *  @see #setHex(boolean)
 */
   public boolean isHex() {
      return this.hex;
   } // invHex()


/** Vertauschen von x und y. <br />
 *  <br />
 *  Diese Methode vertauscht den Inhalt der beiden unteren Stackregister.
 *  t und z bleiben unverändert. <br />
 */
   public void chgXY() {
      double tmp = x;
      x  = y;
      y  = tmp;
      autoEnter = true;
      afterDot = false;
   } // chgXY()


/** Herunterrollen des Stack (t wird urspr. x). <br />
 *  <br />
 *  Diese Methode rollt den Stack nach unten bzw. nach x,
 *  setzt aber den ursprünglichen x-Inhalt nach t.<br />
 *  <br />
 *  Nach viermaliger Anwendung ist der Ausgangszustand wieder erreicht.<br />
 */
   public void rollDown() {
      double tmp = x;
      x  = y;
      y  = z;
      z  = t;
      t  = tmp;
      autoEnter = true;
      afterDot = false;
   } // rollDown()



/** Push-Operation beziehungsweise Enter. <br />
 *  <br />
 *  Diese Methode schiebt den Inhalt des Stacks um eins hoch.
 *  x wird nach y kopiert; der bisherige Inhalt von t geht verloren.<br />
 *  <br />
 *  Im Fehlerzustand wird nur X und der Fehler gelöscht.<br />
 */
   public void enter() {
      enterTo0  = true;
      autoEnter = false;
      afterDot  = false;
      if (error) {
         x = 0;
         error = false;
         errText = null;
         return;
      }
      t = z;
      z = y;
      y = x;
   } // enter()


/** Rücksetzen in den Einschaltzustand. <br />
 *  <br />
 *  Diese Methode löscht alle Stack-Register auf 0 und setzt den
 *  Modus auf Dezimal.<br />
 *  <br />
 */
   public void reset() {
      hex      = false;
      clrAll();
   } // reset


/** Löschen des X-Registers. <br />
 *  <br />
 *  Diese Methode löscht nur das X-Register.<br />
 *  Ein Fehlerzustand wird gelöscht.<br />
 */
   public void clrX() {
      x = 0;
      enterTo0  = true;
      autoEnter = false;
      afterDot  = false;
      error     = false;
      errText   = null;
   } // clrX()

/** Setzen des X-Registers auf einen Wert. <br />
 *  <br />
 *  Diese Methode setzt nur das X-Register.<br />
 *  Ein Fehlerzustand wird gelöscht.<br />
 */
   public void setX(double x) {
      if (autoEnter || ! enterTo0) enter();
      this.x = x;
      enterTo0  = true;
      autoEnter = true;
      afterDot  = false;
      error     = false;
      hex       = false;
      errText   = null;
   } // clrX()

/** Löschen von allen Registern. <br />
 *  <br />
 *  Diese Methode löscht alle Stack-Register auf 0.
 */
   public void clrAll() {
      t = z = y = 0;
      clrX();
   } // clrXclrAll

} // class Rechner (07.11.2001; 23.03.2004; 11.01.2010)   
