import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/** A Java program using a GUI to simulate a simple calculator with
  4 arithmetic operations (+,-,*,/), negate (+/-), and a few
  other keys for clear, backspace, and recall; the program
  maintains one memory buffer for a previously entered number,
  it performs an operation as soon as two operands are entered;
  exceptions of bad number format are caught 
  (Nov. 27, 2001)
*/ 

public class SimpleCalculator extends JFrame {
  /* the display field and buffers */
  private JTextField displayField;
  private StringBuffer displayBuffer, numBuffer;
  private char opBuffer;

  /* calculator buttons and their labels */
  private JButton[] keys;
  private String[] keyLabels;

  /** the constructor which creates the GUI with the
    buttons, set the size of the GUI and make it visible */
  public SimpleCalculator() {
    super("A Simple Calculator"); // give title
    // initialize StringBuffers to the empty string ""
    displayBuffer = new StringBuffer("");
    numBuffer = new StringBuffer("");

    opBuffer = ' ';  // blank character

    // create JButtons with texts and register event handler
    createButtons();  
    // add buttons to a JPanel with GridLayout
    JPanel panel = addButtons();  
 
    // create the GUI with a display field and the buttons
    Container container = getContentPane();
    container.setLayout(new BorderLayout());
    displayField = new JTextField();
    displayField.setEditable(false);
    container.add(displayField, BorderLayout.NORTH); 
    container.add(panel, BorderLayout.CENTER);

    setSize(290, 220);
    setVisible(true);
  }

  /** create a SimpleCalculator instance */
  public static void main(String[] args) {
    SimpleCalculator app = new SimpleCalculator();
    app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  }

  /** create JButtons with texts, and register an event 
    handler with the buttons */
  private void createButtons() {
    // initilaize an array of Strings for button labels
    keyLabels = new String[] { 
                    "0", "1", "2", "+", "RC",
                    "3", "4", "5", "-", "BP",
                    "6", "7", "8", "*", "CL",
                    "9", "+/-", ".", "/", "="
                };
    keys = new JButton[20];  // create 20 JButton references

    // create JButtons with the sepcified String labels
    for (int i = 0; i < 20; i++)
      keys[i] = new JButton(keyLabels[i]);

    // increase font size for operators, +/-, and = 
    Font ft = new Font("Dialog", Font.PLAIN, 17);
    keys[3].setFont(ft);
    keys[8].setFont(ft);
    keys[13].setFont(ft);
    keys[16].setFont(ft);
    keys[18].setFont(ft);
    keys[19].setFont(ft);

    // create and register event handler for buttons
    ButtonHandler handler = new ButtonHandler();
    for (int i = 0; i < 20; i++)
      keys[i].addActionListener(handler);
  }

  /** add buttons to a JPanel with a gridLayout and
    return the JPanel */
  private JPanel addButtons() {
    JPanel panel = new JPanel();
    panel.setLayout(new GridLayout(4,5));

    // add buttons to JPanel
    for (int i = 0; i < 20; i++)
      panel.add(keys[i]);

    return panel;
  }

  /** The button event handler class */
  private class ButtonHandler implements ActionListener {
    public void actionPerformed(ActionEvent e) {

      // get the label of the buuton causing the event
      String label = e.getActionCommand();

      /* handle button events using the logic
         specified in a state transition diagram */
      if (label.equals("RC")) {  // recall numBuffer
        displayBuffer = new StringBuffer(numBuffer.toString());
        displayField.setText(displayBuffer.toString());
      }
      else if (label.equals("BP")) {  // backspace display
        if (displayBuffer.length() != 0 ) {
          displayBuffer.deleteCharAt(displayBuffer.length()-1);
          displayField.setText(displayBuffer.toString());
        }
      }
      else if (label.equals("CL")) {  // clear buffers and display
        numBuffer = new StringBuffer("");
        displayBuffer = new StringBuffer("");
        opBuffer = ' ';
        displayField.setText("");
      }
      else if (label.equals("=")) {  // the "=" key
        handleEqual();
      }
      // operators
      else if (label.equals("+")|| 
               label.equals("-")|| 
               label.equals("*")|| 
               label.equals("/")) {
        handleOperator(label);
      }
      else if (label.equals("+/-")) {  // negate display
        try {
          double number = Double.parseDouble(displayBuffer.toString());
          if (number > 0) {  // negate in this case
            displayBuffer.insert(0, '-');
            displayField.setText(displayBuffer.toString());
          }
          else if (number < 0) {
            displayBuffer.deleteCharAt(0);
            displayField.setText(displayBuffer.toString());
          }
        } catch (NumberFormatException exp) {
             /* do nothing if number is not of the proper format */ }
      }
      else { // must be digit or decimal point
        handleDigit(label);
      }
    }  // end of actionPerformed()

    /** handle a digit or decimal point input */
    private void handleDigit(String digit) {
      // if the display contains a previously entered operator
      if (displayBuffer.toString().equals("+")||
          displayBuffer.toString().equals("-")|| 
          displayBuffer.toString().equals("*")|| 
          displayBuffer.toString().equals("/")) {
        // move operator to opBuffer, then display new input
        opBuffer = displayBuffer.charAt(0);  // this is redundant
        displayBuffer = new StringBuffer(digit);
        displayField.setText(digit);
      }
      else { // otherwise, append new input to display
        displayBuffer.append(digit);
        displayField.setText(displayBuffer.toString());
      }
    }

    /** handle an operator input */
    private void handleOperator(String operator) {
      int code = 0;

      if (!numBuffer.toString().equals("") && opBuffer != ' '
          && !displayBuffer.toString().equals("")) {
        // perform a previously entered operation
        code = handleEqual(); 

        // display new operator if no error
        if (code == 0) {
          displayBuffer = new StringBuffer(operator); 
          displayField.setText(operator);  
          opBuffer = operator.charAt(0);
        }
      }
      else {  // no previous operator saved
        // move the current display into numBuffer
        numBuffer = new StringBuffer(displayBuffer.toString());
        displayBuffer = new StringBuffer(operator); 
        // display new operator and enter it into opBuffer
        displayField.setText(operator);  
        opBuffer = operator.charAt(0);
      }
    } // end of method handleOperator()

    /** handle the "=" key input; return 0 for successful
      operation; return -1 if no operation or if it fails */
    private int handleEqual() {
      // perform a previously entered operation
      try { // prepare for bad number input
        double numOne = Double.parseDouble(numBuffer.toString());
        double numTwo = Double.parseDouble(displayBuffer.toString());
        double result = 0.0;
        switch (opBuffer) {
          case '+': 
            result = numOne + numTwo;
            break;
          case '-': 
            result = numOne - numTwo;
            break;
          case '*': 
            result = numOne * numTwo;
            break;
          case '/': 
            // check for divide by zero
            if (numTwo == 0.0) {
              displayField.setText("Divide by Zero Error");
              return -1;
            }
            result = numOne / numTwo; 
            break;
        }
        if (opBuffer != ' ') {  // if operation performed
          numBuffer = new StringBuffer(String.valueOf(result));
          displayBuffer = new StringBuffer(String.valueOf(result));
          displayField.setText(displayBuffer.toString());
          opBuffer = ' ';  // blank out operator
          return 0;
        }
        else  // no operation performed
          return -1;
      } catch (NumberFormatException exp) {
          if (opBuffer != ' ')  // operation attempted (12/02/01)
            displayField.setText("Number Format Exception");
          return -1;
      }
    } // end of method handleEqual()

  } // end of class ButtonHandler

} // end of class SimpleCalculator 
   

