import java.applet.Applet;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;

import de.frame4j.graf.DisplayPattern;
import de.frame4j.graf.MatrixDisplay;
import de.frame4j.graf.MatrixDisplayDim;
import de.frame4j.graf.MatrixTextDisplay;

/** <b>A tiny whole-number calculator &mdash; stack architecture</b>. <br />
 *  <br />
 *  An object of this class is an Applet, featuring the logic (state machine) 
 *  and the display of an integer stack calculator. It has four stack 
 *  registers (x, y, z and t just like the famous HP pocket 
 *  calculators).<br />
 *  <br />
 *  Decimal and hexadecimal display and entry are supported.<br />
 *  The number range is (Java) long (64 bit, 2s complement).<br />
 *  <br />
 *  This Applet can be used to demonstrate (Java) integer arithmetic as well
 *  as the stack computer architecture.<br />
 *  <br />
 *  The keyboard for digits, &quot;Enter&quot; (the stack push) and the 
 *  operations is not part of the Applet. (In that sense the Applet is a pure
 *  demo of {@link MatrixDisplay}, {@link MatrixTextDisplay} and 
 *  {@link DisplayPattern}.) The (key) entries are supplied by public methods.
 *  The keypad may thus be implemented by  buttons in the &quot;forms&quot; 
 *  of an embedding HTML page.<br />
 *  <br />
 *  See the minimal demo page
 *  <a href="./de/frame4j/graf/doc-files/calculator.html">calculator.html</a>
 *  and the
 *  <a href="./de/frame4j/graf/doc-files/Calculator.java">source</a>.<br />
 *  <br /> 
 *  Copyright 2001, 2003, 2004, 2009 &nbsp; Albrecht Weinert <br /> 
 *  <br />
 *  @see MatrixDisplay
 *  @see DisplayPattern
 *
 *  @author   Albrecht Weinert
 *  @version  $Revision: 46 $ ($Date: 2010-04-28 16:41:26 +0200 (Wed, 28 Apr 2010) $)
 */
 //  so far:   V00.00 (09.01.2001 16:46) : new
 //            V00.01 (10.01.2001 11:22) : long, Error, Version
 //            V00.02 (11.01.2001 08:39) : rollDown, thrOn locks
 //            V00.03 (22.01.2001 14:18) : clrXclrAll
 //            V00.04 (07.11.2001 10:36) : /**
 //            V02.00 (23.04.2003 15:27) :  CVS Eclipse
 //            V02.09 (28.10.2003 17:45) : MatrixTextDisplay assoc., dir.paint
 //            V.107+ (12.05.2009 16:05) : ported to Frame4J
 //            V.185+ (18.01.2010 14:03) : SVN resync.
    
public class Calculator extends Applet {

/** Name, version, copyright. <br /> */
   public static final String NVC = 
   //     123456789.123456789.123456789.123456789.123456789.123456789.1234567
         " Whole numbered stack calculator   V.$Revision: 46 $ (18.01.2010)  "
                   + "\u00A9 Albrecht Weinert  "; 

/** Main register X (=display register). <br /> */
   protected long x;

/** Stack register Y (Stack is x, y, z, t). <br /> */
   protected long y;

/** Stack register Z (Stack is x, y, z, t). <br /> */
   protected long z;

/** Stack register T (Stack is x, y, z, t). <br /> */
   protected long t;


   private static final byte _ = 0;
   private static final byte X = 1;
   
/** Display pattern for minified (5*3) H. <br />
 *  <br/>
 *  Used as &quot;Hex&quot; marker.<br />
 */   
   protected static final byte[][] MINI_H =  new byte[][]{
      {_,_,_,_,_},
      {_,_,_,_,_},
      {_,_,_,_,_},
      {X,_,X,_,_},
      {X,_,X,_,_},
      {X,X,X,_,_},
      {X,_,X,_,_},
      {X,_,X,_,_}   };

/** Display pattern for minified (5*3) E. <br />
 *  <br/>
 *  Used as &quot;Error&quot; marker.<br />
 */   
   protected static final byte[][] MINI_E =  new byte[][]{
      {_,_,_,_,_},
      {_,_,_,_,_},
      {_,_,_,_,_},
      {X,X,X,_,_},
      {X,_,_,_,_},
      {X,X,X,_,_},
      {X,_,_,_,_},
      {X,X,X,_,_}   };

/** Number of 5*8 elements in display. <br />
 *  <br />
 *  Value: {@value}
 */
   public static final int ANZ_EL = 20;


/** The display. <br />
 *  <br />
 *  Die display itself will be implemented as a {@link MatrixTextDisplay}
 *  object.<br />
 *  <br />
 *  It will be made for one line of 20 ({@link #ANZ_EL}) 5 * 8 elements with
 *  2 dots horizontal spacing. The matrix dots itself are sized 4 * 4 pixels
 *  and are densely packed.<br />
 *  <br />
 *  Thus the applet's minimal size is about 596 * 60 (width * height).<br />
 */
   protected MatrixTextDisplay md;

/** Initialising. <br /> */
   @Override public void init() {
      md = MatrixTextDisplay.make(1, 20, 2, 0,
                       new MatrixDisplayDim(5,  8 ,
                       23, 14, // margin
                       4, 4,   4, 4)); // 
      this.setBackground(Color.lightGray); 
      md.setBackground(Color.lightGray);  // gray would be default
      md.setMarginGround(new Color(176, 176, 176)); // yellow would be default
 
      //  md.setForeground(Color.red);    // rt = red is the default (no need)
      md.putC( '0', 0, ANZ_EL - 1);
      mySize = getSize();
      /// System.out.println("   /// Applet calculator: init " + md);
      reset();
   } // init()

   Dimension mySize;

   
//---------------------  Direct paint --------------------------------

/** Update the display. <br />
 *  <br />
 *  This method does not clear the background (to background colour) as 
 *  {@link #paint paint()} will update all matrix dots without
 *  flickering.<br />
 */
   @Override public void update(Graphics g) {
     md.paint(g, null, mySize);
  } // update

/** Paint the display. <br />
 *  <br />
 *  This method is equivalent to {@link #update  update()}. <br />
 */
   @Override public void paint(Graphics g){ update(g); }

/** Renew the X display. <br /> */
      public void showX() {
         if (thrOn) return;
         if (x == 0) {
            md.clear();
            md.putC('0', 0, ANZ_EL - 1);
         } else {
             md.putStringR(hex ?  Long.toHexString(x).toUpperCase()
                               :  Long.toString(x), 20, 0);
         }
         if (hex)
            md.setDots(0, 0, 5, 8 , MINI_H, 0, 0);
         this.repaint();
      } // showX()



/** State: Hex calculator. <br /> */
   protected boolean hex;

/** State: next digit entry will clear X before. <br /> */
   protected boolean enterTo0 = true;

/** State: next digit entry will do an Enter (push stack) before. <br /> */
   protected boolean autoEnter;

/** Entry of a digit (0..9; ..15). <br />
 *  <br />
 *  This method has to be called with a parameter of 0..9..15, according to
 *  the digit key 0..9, A..F pressed.<br />
 *  <br />
 *  The display will be updated accordingly. After a previous calculation /
 *  operation {@link #enter()} will be processed before.<br />
 *  <br />
 *  Implementation hint: As this method implements the numeric key pad of a
 *  pocket calculator it assumes that the number being entered / constructed
 *  in stack register x is not negative. (Changing {@link #chgSign() sign}
 *  would be an operation that ends the number entry.)<br />
 *  <br />  
 *  @param digit The digit key entry: 0..9 decimal respectively 0..15 if 
 *         {@link #hex hexadecimal}. Values out of range will be ignored. 
 */
   public void digIn(int digit) {
      if (thrOn || digit < 0 || digit > 15 || digit >= 10 && !hex) return;
      if (autoEnter) enter();
      boolean was0 = enterTo0 || x == 0;
      enterTo0 = false;
      if (was0) {
         md.clear();
         x = digit;
      } else {
         if (hex) {
             if ( (x & 0xF000000000000000L) != 0) { 
               md.setDots(5, 0, 5, 8 , MINI_E, 0, 0);
               this.repaint();
               return;
             }
             x = x * 16 + digit;
         } else {
             long tmp = x * 10 + digit;
             if (tmp < 0) { 
               md.setDots(0, 0, 5, 8 , MINI_E, 0, 0);
               this.repaint();
               return;
             }
             x = tmp;
         }
      }
      md.putCR(digit >= 10 ?  (char)('A' - 10 + digit) : (char)('0' + digit), 0);
      if (hex)
         md.setDots(0, 0, 7, 8 , MINI_H, 0, 0);
      this.repaint();
   } // digIn(int)


/** Multiplication x = x * y. <br />
 *  <br />
 *  This method uses the two lower stack registers as operands popping them
 *  and pushing the result. (t is copied to z as side effect).<br />
 */
   public void multiply() {   
      x *= y;
      y = z;
      z = t;
      autoEnter = true;
      showX();
   } // multiply()

/** Division (integer) x = y / x. <br />
 *  <br />
 *  This method uses the two lower stack registers as operands popping them
 *  and pushing the result. (t is copied to z as side effect).<br />
 */
   public void divide() {
      if (thrOn) return;
      try {
         x = y / x;
         y = z;
         z = t;
         autoEnter = true;
         showX();
      } catch (Exception e) {
         md.putStringR("Error: " + e.getMessage(), 18, 0);
         enterTo0 = true;
         this.repaint();
      } 
   } // divide()


/** Division and remainder (integer) x,y  = (y / x), (x mod y). <br />
 *  <br />
 *  This method calculates the integer quotient y / x and the integer
 *  remainder y mod x (y % x in Java). The new content of x is the quotient
 *  and y will be the remainder. By exchanging {@link #chgXY()} they can be
 *  viewed alternately.<br />
 *  Registers z and t remain unchanged. <br />
 */
   public void divide2() {
      if (thrOn) return;
      try {
         long tempX = y / x;
         long tempY = y % x; // 2 temporary variables cause of Except.
         y = tempY;
         x = tempX;
         autoEnter = true;
         showX();
      } catch (Exception e) {
         md.putStringR("Error: " + e.getMessage(), 18, 0);
         enterTo0 = true;
         this.repaint();
      } 
   } // divide2()


/** Modulo (integer remainder) x = y % x. <br />
 *  <br />
 *  This method uses the two lower stack registers as operands popping them
 *  and pushing the result. (t is copied to z as side effect).<br />
 *  The remainder calculation uses Java y % x (sign) rules as they 
 *  (surprisingly to many) are.<br />
 */
   public void mod() {
     if (thrOn) return;
     try {
         x = y % x;
         y = z;
         z = t;
         autoEnter = true;
         showX();
      } catch (Exception e) {
         md.putStringR("Error: " + e.getMessage(), 18, 0);
         enterTo0 = true;
         this.repaint();
      } 
   } // mod()


/** Exponent (y power x) x = y ** x. <br />
 *  <br />
 *  This method uses the two lower stack registers as operands popping them
 *  and pushing the result. (t is copied to z as side effect).<br />
 */
   public void yHx() {
     if (thrOn) return;
      try {
         x = (long)Math.pow(y, x);
         y = z;
         z = t;
         autoEnter = true;
         showX();
      } catch (Exception e) {
         md.putStringR("Error: " + e.getMessage(), 18, 0);
         enterTo0 = true;
         this.repaint();
      } 
   } // yHx() 


/** Addition x = y + x. <br /> */
   public void plus() {
      x += y;
      y = z;
      z = t;
      autoEnter = true;
      showX();
   } // plus()


/** Subtraction x = y - x. <br />  */
   public void minus() {
      x = y - x;
      y = z;
      z = t;
      autoEnter = true;
      showX();
   } // minus()


/** Change the sign of x:   x = -x. <br />
 *  <br />
 *  The other stack registers remain unchanged.<br />
 */
   public void chgSign() {
     if (thrOn) return;
      x = -x;
      autoEnter = true;
      showX();
   } // chgSign()

/** Hex-Decimal toggle. <br />
 *  <br />
 *  This method changes the display and entering mode from decimal to
 *  hexadecimal and vice versa.<br />
 */
   public void invHex() {
      if (thrOn) return;
      hex = !hex;
      autoEnter = true;
      showX();
   } // invHex()


/** Exchange x and y. <br />
 *  <br />
 *  This method interchanges the content of x and y, leaving z and t 
 *  unchanged.<br />
 */
   public void chgXY() {
      long tmp = x;
      x  = y;
      y  = tmp;
      autoEnter = true;
      showX();
   } // chgXY()


/** Roll down the stack (t becomes the previous x). <br />
 *  <br />
 *  This method rolls the stack down or, to say it unambiguously, toward x. 
 *  The former x content is put to t.<br />
 *  <br />
 *  Calling this four times restores the original condition.<br />
 */
   public void rollDown() {
      long tmp = x;
      x  = y;
      y  = z;
      z  = t;
      t  = tmp;
      autoEnter = true;
      showX();
   } // rollDown()


/** Push operation respectively Enter. <br />
 *  <br />
 *  This method pushes the stack one up (towards t). The former t content is
 *  lost and x and y have now the same content.<br />
 *  But x will be {@link #enterTo0 auto-cleared} before the next 
 *  digit entry.<br />
 */
   public void enter() {
      enterTo0 = true;
      autoEnter = false;
      if (thrOn) {
       //  System.out.println("   ///    Applet Calculator: enter (thrOn)");
         thrOn = md.stopRunText();
         t = z = y = x = 0;
         hex       = false;
         showX();
      } else {
         //System.out.println("   ///    Applet Calculator: enter (thrOff)");
         t = z;
         z = y;
         y = x;
      }
   } // enter()


/** Reset to &quot;power on&quot; state. <br />
 *  <br />
 *  This method clears all stack registers to 0, clear the error flag and sets
 *  the decimal mode.<br />
 *  <br />
 *  Additionally marquee (running) display of the version info is 
 *  started.<br />
 *  Enter ({@link #enter()}) starts the normal operation.<br />
 *  <br />
 */
   public void reset() {
     //  System.out.println("   ///    Applet Rechner: reset");
      t = z = y = x = 0;
      enterTo0 = true;
      hex      = false;
      if (thrOn) return;
      thrOn    = md.startRunText(NVC, this, 44);
   } // reset

/** Clear X or all registers. <br />
 *  <br />
 *  This method just sets x to 0 if not yet so. Otherwise all registers
 *  are set to 0. <br />
 */
   public void clrXclrAll() {
      if (thrOn) return;
      if (x != 0) 
         x = 0;
      else
         t = z = y = 0;
      enterTo0  = true;
      autoEnter = false;
      showX();
   } // clrXclrAll

/** Do the marquee (running) version display. <br /> */
   protected boolean thrOn;

} // class Calculator (07.11.2001, 23.03.2004, 02.01.2010)   
