/***********************************************************

Taken from https://lexzeus.tripod.com/
(c) by LexZEUS - Alexander Yanuar Koentjara

To use: 
1. Copy and paste the code to calculator.java
2. Compile it: javac calculator.java
3. Create a calc.html code as below :

<html>
<body>
  <applet code=calculator.class height=250 width=200>
  This is applet
  </applet>
</body>
</html>

4. Open the html file using your browser, or use command: appletviewer calc.html

***********************************************************/

import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;

public class calculator extends Applet
       implements MouseListener, KeyListener, MouseMotionListener
{
  private static Font mainFont = new Font("Arial", Font.PLAIN, 20 );
  private static Color CBack = new Color(85,85,85);

  private static final String aboutStr = " ***************************** \n" +
                                         " * Simple shaking calculator * \n" +
                                         " * By: Alexander Yanuar K.   * \n" +
                                         " *     31-Oct-2001           * \n" +
                                         " *                           * \n" +
                                         " *                           * \n" +
                                         " * A simple way for java     * \n" +
                                         " * learner to study about    * \n" +
                                         " * applet, inner class, and  * \n" +
                                         " * Thread ...                * \n" +
                                         " *                           * \n" +
                                         " ***************************** \n";

  private static final int _width=200;   // calculator width
  private static final int _height=250;  // calculator height
  private static final int _cubeSize=40; // button size
  private static final int _leftMargin=20; // button left margin
  private static final int _topMargin=60; // button top margin
  private static final int _leftFontMargin=13; // font left margin
  private static final int _topFontMargin=25; // font top margin

  private static final Color colDarkRed = new Color (128,0,0);
  private static final Color colNavy    = new Color (0,0,128);

  static final CalcControl nullOperator =
                           new CalcControl("=",false);

  private static final CalcControl[] enumControl = {
                 new CalcControl("9",true), new CalcControl("8",true),
                 new CalcControl("7",true), new CalcControl("+",false),
                 new CalcControl("6",true), new CalcControl("5",true),
                 new CalcControl("4",true), new CalcControl("-",false),
                 new CalcControl("3",true), new CalcControl("2",true),
                 new CalcControl("1",true), new CalcControl("*",false),
                 new CalcControl("0",true), new CalcControl("C",false),
                 nullOperator, new CalcControl("/",false)
                 };

  int globalPressed=-1;              // what the button was previously
                                     // pressed

  int globalCursor=-1;               // where the mouse pointer is ...
                                     // the value is the first index of
                                     // enumControl array (0-15)
                                     // -1 means point nowhere ...

  private long opr1, opr2 = 0;       // operand1 and operand2
  private String opr1_str = "", opr2_str = "";
  private boolean swapFlag = true;
  public int sx=0, sy=0;

  CalcControl currentOperator=nullOperator;
                                     // no operator at the very first time
                                     // same effect is user press "C" button

  Image myBuff;                      // double buffering
  CalcThread animation;

  public void init() {
    addMouseListener(this);
    addMouseMotionListener(this);
    addKeyListener(this);
    myBuff = createImage(_width,_height);
    animation = new CalcThread(this);
  }

  void trigger(int key)
  {
     globalPressed=key;
     calculate(key);
     animation.enableShaking();
     repaint();
  }

  public void keyReleased(KeyEvent me) { /* do nothing */ }
  public void keyTyped(KeyEvent me)    { /* do nothing */ }
  public void keyPressed(KeyEvent e)
  {
    int code = e.getKeyCode();
    switch (code)
       {
       case KeyEvent.VK_C: trigger(13); break;
       case KeyEvent.VK_0: trigger(12); break;
       case KeyEvent.VK_1: trigger(10); break;
       case KeyEvent.VK_2: trigger(9); break;
       case KeyEvent.VK_3: trigger(8); break;
       case KeyEvent.VK_4: trigger(6); break;
       case KeyEvent.VK_5: trigger(5); break;
       case KeyEvent.VK_6: trigger(4); break;
       case KeyEvent.VK_7: trigger(2); break;
       case KeyEvent.VK_8: trigger(1); break;
       case KeyEvent.VK_9: trigger(0); break;
       }

    char ch = e.getKeyChar();
    if (ch=='=')
       trigger(14);
    else
    if (ch=='+')
       trigger(3);
    else
    if (ch=='-')
       trigger(7);
    else
    if (ch=='/')
       trigger(15);
    else
    if (ch=='*')
       trigger(11);
  }

  public String getAppletInfo() {
    return aboutStr; 
  }

  public void mouseDragged(MouseEvent me) { /* do nothing */ }
  public void mouseMoved(MouseEvent me) {
     int x=me.getX()-_leftMargin,
         y=me.getY()-_topMargin;

     if (x<0 || x>_cubeSize*4-5 ||
         y<0 || y>_cubeSize*4-5 ) {
        globalCursor=-1;
        repaint();
        return;
        }


     x=(int) (Math.floor(x/_cubeSize));
     y=(int) (Math.floor(y/_cubeSize));

     globalCursor = y*4+x;
     repaint();
  }

  public void mouseEntered(MouseEvent me) { /* do nothing */ }
  public void mouseExited(MouseEvent me)  { /* do nothing */ }
  public void mouseClicked(MouseEvent me) { /* do nothing */ }
  public void mouseReleased(MouseEvent me) { /* do nothing */ }

  public void mousePressed(MouseEvent me) {
    if (globalCursor<0) return;
    if ((me.getModifiers() & MouseEvent.BUTTON1_MASK )!=0)
       {
       trigger(globalCursor);
       }
  }

  void reset() {
    opr1 = 0;
    opr2 = 0;
    currentOperator = nullOperator;
    opr1_str="";
    opr2_str="";
  }

  private void calculate(int key)
  {
    CalcControl calc = enumControl[key];

    if (calc.isNumber) // the pressed button is a number
       {
       if (swapFlag)
          {
          swapFlag=false;
          opr1 = opr2;
          opr1_str = opr2_str;
          opr2_str = "";
          }
       opr2_str += calc.representation;
       opr2 = (new Long(opr2_str)).longValue();
       }
    else               // the pressed button is an operator
       {
       swapFlag=true;

       if (calc.representation.equals("C"))
          {
          reset();
          return;
          }

       try {
           opr2 = currentOperator.doCalc(opr1,opr2);
           }
       catch (Exception ex)
           {
           System.out.println(ex);
           }

       opr2_str = (new Long(opr2)).toString();
       currentOperator=calc;
       }
  }

  public void start() {
     setBackground(Color.white);
  }

  public void destroy() {
     animation.running=false;
     super.destroy();
  }

  public void update(Graphics scGR) {
    paint(scGR);
  }

  private void drawAll(Graphics Buff)
  {
    // DRAWING THE BACKGROUND:
    Buff.setColor(Color.lightGray);
    Buff.fillRect(0,0,_width,_height);
    Buff.setFont(mainFont);

    // DRAWING THE BUTTONS:
    int num=0;
    int sx2,sy2;
    for (int y=0;y<4;y++)
        for (int x=0;x<4;x++)
            {
            sx2=0;
            sy2=0;
            if (num!=globalCursor)
               Buff.setColor(colDarkRed);
            else
               Buff.setColor(Color.red);

            if (num==globalPressed)
               {
               sx2=sx;
               sy2=sy;
               }

            Buff.fillRect(_leftMargin+_cubeSize*x+sx2,
                          _topMargin+_cubeSize*y+sy2,
                          _cubeSize-3,_cubeSize-3);

            if (enumControl[num].isNumber)
               Buff.setColor(Color.white);
            else
               Buff.setColor(Color.yellow);

            Buff.drawString( enumControl[num++].representation,
                             _leftMargin+_leftFontMargin+x*_cubeSize,
                             _topMargin+_topFontMargin+y*_cubeSize);
            }


    // DRAWING THE DIGITS:
    int maxwidth = _width-_leftMargin*2-3;
    Buff.setColor(Color.white);
    Buff.fillRect(_leftMargin,10,maxwidth,_topMargin-10-5);
    Buff.setColor(colNavy);
    String digits = null;
    if (!opr2_str.equals(""))
       digits=opr2_str;
    else
       digits="0";

    FontMetrics metrics = Buff.getFontMetrics();
    int wd = metrics.stringWidth(digits);

    if (wd<maxwidth)
       Buff.drawString(digits,_leftMargin+(maxwidth)-10-wd,40);
    else
       Buff.drawString("Too big",_leftMargin+5,40);
  }

  public void paint(Graphics scGR) {
    Graphics Buff = myBuff.getGraphics();
    drawAll(Buff);
    scGR.drawImage(myBuff,1,1,this);
  }

  class CalcThread extends Thread {
     public int shaking=0;
     boolean running=true;
     calculator calc;

     CalcThread(calculator _calc)
     {
        calc=_calc;
        this.start();
     }

     public void enableShaking()
     {
       shaking=25;
     }

     public void run()
     {
       while (running)
       {
          shaking--;
          if (shaking>1)
             {
             calc.sx=(int) Math.round(Math.random()*5)-2;
             calc.sy=(int) Math.round(Math.random()*5)-2;
             calc.repaint();
             }
          else
             {
             calc.sx=0;
             calc.sy=0;
             calc.globalPressed=-1;
             if (shaking==1)
                calc.repaint();
             shaking=0;
             }
          try {
              this.sleep(10);
              }
          catch (Exception ex) { /* do nothing */ }
       }
     }

  }

  static class CalcControl { // inner class for individual button and
                             // the meaning of each button

      public String representation = null;
      public boolean isNumber;

      CalcControl(String _rep, boolean _isNumber)
      {
         representation=_rep;
         isNumber=_isNumber;
      }

      long doCalc(long opr1, long opr2)
             throws Exception
      {
        if (isNumber)
           throw new Exception("Number is not operator");

        if (representation.equals("="))
           return opr2;
        else
        if (representation.equals("C"))
           {
           return 0;
           }
        else
        if (representation.equals("+"))
           return opr1+opr2;
        else
        if (representation.equals("-"))
           return opr1-opr2;
        else
        if (representation.equals("*"))
           return opr1*opr2;
        else
        if (representation.equals("/"))
           return Math.round(opr1/opr2);

        throw new Exception("Unknown operator");
      }
  }

}