/*  Java Applet source code for "RPN calculator on Java"
 * 
 *    This is an applet to realize "RPN caculator" on a Web browser.
 *    The author, T. Nakatsuka, does not insist any right on this program,
 *  i.e., free to re-distribute to any place with/without any modification.
 *    The author acknowledges Hewlett-Packard Corp. for their intelligent
 *  product of micro-calculators, which were borne in mind during designing.
 */

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

public class calcj extends Applet implements MouseListener, 
 KeyListener, ItemListener, FocusListener {

  Image barea; Font font, fontb;
  CheckboxGroup degrad; Checkbox degree, radian; int kdeg;
  double y, z, t; Label regY, regZ, regT;
  double x; TextField regX; StringBuffer xstr;
  int key, kf, l, lce; double r, tmp;
/*
 *  key : Status for Register-X
 *      = 0 : Register-X shall be cleared by New-Value entry
 *             [after ENTER/CLR/CLX/STO operation]
 *      = 1 : CHS input after key=0
 *      = 2 : Register-X shall be pushed (on stack) by New-Value entry
 *             [after Arithmetic or SWAP/ROT operation,
 *              or definite value entry by RCL/'pi'/'e']
 *      = 3 : CHS input after key=2
 *      =4-7: on the way of New-Value entry
 *             [after Numeric input keys (0-9/./EEX)]
 *            =4 : neither . nor EEX input yet
 *            =5 : . already input but EEX not yet
 *            =6 : EEX already input but no exponent digit yet
 *            =7 : at least 1 exponent digit input already
 *      = 9 : error detected (use CLR/CLX to reset)
 */

  String sz(String v) {
    int len = v.length();
    if ((v.charAt(--len) != '0') || (v.charAt(--len) != '.')) { return v; }
    else { return v.substring(0,len); }
  }
  void showt() { regT.setText(sz(Double.toString(t))); }
  void showz() { regZ.setText(sz(Double.toString(z))); }
  void showy() { regY.setText(sz(Double.toString(y))); }
  void showx() {
    if (Double.isNaN(x) || Double.isInfinite(x)) error();
    else {
      String s = sz(Double.toString(x));
      regX.setText(s); xstr.setLength(0); xstr.append(s);
      l = s.length(); key=2;
    }
  }
  void push(int k) { t = z; z = y; if (k==0) y = x; else y = -x;
                     showy(); showz(); showt(); }
  void pop() { x = y; y = z; z = t; showz(); showy(); showx(); }
  void rad0() { if (kdeg==1) x /= Math.PI/180.; }
  void rad1() { if (kdeg==1) x *= Math.PI/180.; }
  void num(int i) {
    if (key<4) {
      if (key==2) push(0); else if (key==3) push(1);
      xstr.setLength(0); l=0;
      if ((key==1) || (key==3)) { xstr.append("-"); l++; }
      key=4;
    }
    xstr.append(Integer.toString(i)); l++;
    if (key==6) key=7;
    x = Double.valueOf(xstr.toString()).doubleValue();
    regX.setText(xstr.toString());
  }
  void dot() {
    if (key<4) {
      if (key==2) push(0); else if (key==3) push(1);
      xstr.setLength(0); x = 0.;
      if ((key==1) || (key==3)) { xstr.append("-0"); l=2; }
      else { xstr.append("0"); l=1; }
    }
    xstr.append("."); l++; key=5;
    regX.setText(xstr.toString());
  }
  void eex() {
    if (key<4) {
      if (key==2) push(0); else if (key==3) push(1);
      xstr.setLength(0);
      if ((key==1) || (key==3)) { xstr.append("-1."); x = -1.; l=3; }
      else { xstr.append("1."); x = 1.; l=2; }
    }
    xstr.append("E"); lce=l++; key=6;
    regX.setText(xstr.toString());
  }
  void chs() {
    if (key<6) {
      if (key==0) key=1; else if (key==1) key=0;
      if (key==2) key=3; else if (key==3) key=2;
      if (xstr.charAt(0) == '-') {
        String s = xstr.toString().substring(1);
        --l; xstr.setLength(0); xstr.append(s);
      }
      else { xstr.insert(0, "-"); l++; }
      x = -x;
    }
    else if (key==6) {
      if (xstr.charAt(lce) == 'E') { lce++; xstr.append("-"); l++; }
      else { xstr.setLength(--l); lce--; }
    }
    else if (key==7) {
      if (xstr.charAt(lce) == 'E') { xstr.insert(++lce, "-"); l++; }
      else {
        String s = xstr.toString().substring(lce+1);
        l--; xstr.setLength(lce); xstr.append(s); lce--;
      }
      x = Double.valueOf(xstr.toString()).doubleValue();
    }
    regX.setText(xstr.toString());
  }
  void error() { regX.setText(" E R R O R !!"); x = 0.; key=9; }

/***************************************************************************
 *   The Register-X field can be pasted by any value, which is evaluated   *
 *  at the "focusGained" event when coming back from regX TextField.       *
 *  Accordingly, the process of literal input to Register-X is finalized   *
 *  by the temporal loss of focus, which may cause unexpected result.      *
 ***************************************************************************/
  public void focusGained(FocusEvent fe) {
    kf = 1; repaint();
    try {
      String s = regX.getText();
      if (s.length() == 0) x = 0.;
      else x = Double.valueOf(s).doubleValue();
      showx();
    } catch (NumberFormatException nfe) { error(); }
  }                        
  public void focusLost(FocusEvent fe) { kf = 0; repaint(); }
  public void itemStateChanged(ItemEvent ie) {
    if (((Checkbox) ie.getItemSelectable()) == degree)
      { kdeg = 1; requestFocus(); }
    else { kdeg = 0; requestFocus(); }
  }
  public void keyPressed(KeyEvent ke) {}
  public void keyReleased(KeyEvent ke) {}
  public void keyTyped(KeyEvent ke) {
    char kc = ke.getKeyChar();
    if (key < 9) {
      switch (kc) {                /* Only limited key inputs are supported */
        case '0': num(0); break;                  /* '0'-'9' */
        case '1': num(1); break;
        case '2': num(2); break;
        case '3': num(3); break;
        case '4': num(4); break;
        case '5': num(5); break;
        case '6': num(6); break;
        case '7': num(7); break;
        case '8': num(8); break;
        case '9': num(9); break;
        case '^': chs(); break;                   /* ^ for ch +/- */
        case 'E':                                 /* E(e) for Eex */
        case 'e': if (key<6) eex(); break;
        case '.': if (key<5) dot(); break;        /* decimal point */
        case '*': y *= x; pop(); break;           /* arithmetic *,/,+,- */
        case '/': y /= x; pop(); break;
        case '+': y += x; pop(); break;
        case '-': if (key == 6) chs();            /* Only after Eex, '-'   */
                  else { y -= x; pop(); }         /* may be used to ch +/- */
                  break;
        case KeyEvent.VK_ENTER:                   /* 'Enter' or 'ctrl-R'   */
        case '\r': showx(); break;                /*  finalizes regX input.*/
      }
    }
  }
  public void mouseEntered(MouseEvent e) {}
  public void mouseExited(MouseEvent e) {}
  public void mousePressed(MouseEvent e) {}
  public void mouseReleased(MouseEvent e) {}
  public void mouseClicked(MouseEvent e) {
    int fx, fy, m;
    int px = e.getX() - 12; int py = 337 - e.getY();
    if ((px<0) || (205<px) || (py<0) || (195<py)) m = 20;
    else {
      fx = px%42; fy = py%25; m = (px-fx)/42 + (py-fy)/5;
      if (m == 20) m = 21;
      else if ((37<fx) || (20<fy)) m = 20;
    }
    if      (m == 20) {}
    else if (m == 19) { x = 0.; showx(); key=0; }                 /* CLX */
    else if (m == 24) { x = 0.; y = 0.; z = 0.; t = 0.;           /* CLR */
                        showx(); showy(); showz(); showt();
                        r = 0.; key=0; }
    else if (key < 9) {
      switch (m) {
        case 22: chs(); break;                                    /* ch +/- */
        case 23: if (key<6) eex(); break;                         /* Eex */
        case  2: if (key<5) dot(); break;                  /* decimal point */
        case  1: num(0); break;                      /* numerical digit 0-9 */
        case  6: num(1); break;
        case  7: num(2); break;
        case  8: num(3); break;
        case 11: num(4); break;
        case 12: num(5); break;
        case 13: num(6); break;
        case 16: num(7); break;
        case 17: num(8); break;
        case 18: num(9); break;
        case  3: if (key==3) push(1); else if (key>=2) push(0);   /* 'PI' */
                 x = Math.PI; if ((key==1) || (key==3)) x = -x;
                 showx(); break;
        case  4: if (key==3) push(1); else if (key>=2) push(0);   /* 'e' */
                 x = Math.E; if ((key==1) || (key==3)) x = -x;
                 showx(); break;
        case 14: r = x; showx(); key=0; break;                    /* STO */
        case  9: if (key==3) push(1); else if (key>=2) push(0);   /* RCL */
                 x = r; if ((key==1) || (key==3)) x = -x;
                 showx(); break;
        case  0: y += x; pop(); break;                /* arithmetic +,-,*,/ */
        case  5: y -= x; pop(); break;
        case 10: y *= x; pop(); break;
        case 15: y /= x; pop(); break;
        case 25: if (y <= 0.) error();                            /* y**x */
                 else { y = Math.exp(Math.log(y)*x); pop(); }
                 break;
        case 21: push(0); showx(); key=0; break;                  /* Enter */
        case 29: tmp = x; pop(); t = tmp; showt(); break;         /* rot */
        case 28: tmp = x; x = y; y = tmp;                         /* swap */
                 showy(); showx(); break;
        case 26: x = 1./x; showx(); break;                        /* 1./x */
        case 27: x = Math.sqrt(x); showx(); break;                /* sqrt */
        case 30: x = Math.exp(x); showx(); break;                 /* exp */
        case 31: x = Math.log(x); showx(); break;                 /* ln */
        case 35: x = Math.pow(10., x); showx(); break;            /* 10**x */
        case 36: if (x <= 0.) error();                            /* Log10 */
                 else { x = Math.log(x)/Math.log(10.); showx(); }
                 break;
        case 32: rad1(); x = Math.sin(x); showx(); break;         /* sin */
        case 33: rad1(); x = Math.cos(x); showx(); break;         /* cos */
        case 34: rad1(); x = Math.tan(x); showx(); break;         /* tan */
        case 37: if ((x>1.) || (x<-1.)) error();                  /* arcsin */
                 else { x = Math.asin(x); rad0(); showx(); }
                 break;
        case 38: if ((x>1.) || (x<-1.))  error();                 /* arccos */
                 else { x = Math.acos(x); rad0(); showx(); }
                 break;
        case 39: x = Math.atan(x); rad0(); showx(); break;        /* arctan */
      }
    }
  }

  public void paint(Graphics g) {
    g.setColor(Color.lightGray);
    g.fillRect(0, 0, 230, 30);
    g.setColor(Color.black); g.setFont(font);
    g.drawString("Angle:", 35, 22);
    g.fillRect(0, 30, 230, 320);
    g.setColor(Color.white); g.setFont(fontb);
    g.drawString("T", 10, 53); g.drawString("Z", 10, 78);
    g.drawString("Y", 10,103); g.drawString("X", 10,128);
    g.drawImage(barea, 10,140, this);
    if (kf == 0) {
      g.setColor(Color.red); g.fillRect(0, 0, 90, 35);
      g.setColor(Color.white); g.drawString("Focus", 3, 17);
      g.drawString("Lost", 53, 17); g.drawString("click me!", 9, 32);
    }
  }
  public void init() {
    String courier = "Courier";
    fontb = new Font(courier, Font.BOLD, 16);
    font = new Font(courier, Font.PLAIN, 16);
    barea = getImage(getDocumentBase(), "calc.gif");
    setLayout(null);
    regT = new Label("0", Label.LEFT);
    regT.setSize(194,18);
    regT.setLocation(26,36);
    regT.setFont(font);
    add(regT);
    regZ = new Label("0", Label.LEFT);
    regZ.setSize(194,18);
    regZ.setLocation(26,61);
    regZ.setFont(font);
    add(regZ);
    regY = new Label("0", Label.LEFT);
    regY.setSize(194,18);
    regY.setLocation(26,86);
    regY.setFont(font);
    add(regY);
    regX = new TextField(30);
    regX.setSize(195,22);
    regX.setLocation(25,110);
    regX.setFont(font);
    regX.setText("0");
    add(regX);
    degrad = new CheckboxGroup();
    degree = new Checkbox("Degree", true, degrad);
    degree.setSize(58,14);
    degree.setLocation(90,10);
    add(degree); kdeg = 1;
    degree.addItemListener(this);
    radian = new Checkbox("Radian", false, degrad);
    radian.setSize(58,14);
    radian.setLocation(150,10);
    add(radian);
    radian.addItemListener(this);
    addMouseListener(this);
    addKeyListener(this); requestFocus();
    addFocusListener(this); kf = 1;
    xstr = new StringBuffer(32);
    xstr.setLength(0); xstr.append("0"); l=1; lce=0;
    x = 0.; y = 0.; z = 0.; t = 0.; r = 0.; key=0;
  }
}