import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JLabel;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;

/**
   This frame contains a panel that displays buttons
   for a calculator and a panel with a text fields to
   specify the result of calculation.
*/
public class CalculatorFrame extends JFrame
{  
   private JLabel display;
   private JCheckBox radianCheckBox;
   private JRadioButton baseeButton;
   private JRadioButton base10Button;
   private JRadioButton base2Button;
   private JComboBox mathOpCombo;
   private JButton mathOpButton;

   private double lastValue;
   private String lastOperator;
   private boolean startNewValue;   

   private static final int FRAME_WIDTH = 400;
   private static final int FRAME_HEIGHT = 300;

   public CalculatorFrame()
   {
      createButtonPanel();
      createControlPanel();

      display = new JLabel("0");
      add(display, BorderLayout.NORTH);

      lastValue = 0;
      lastOperator = "=";
      startNewValue = true;

      setSize(FRAME_WIDTH, FRAME_HEIGHT);
   }
      
   /**
      Creates the control panel with the text field 
      and buttons on the frame.
   */
   private void createButtonPanel()
   {      
      JPanel buttonPanel = new JPanel();
      buttonPanel.setLayout(new GridLayout(4, 4));

      buttonPanel.add(makeDigitButton("7"));
      buttonPanel.add(makeDigitButton("8"));
      buttonPanel.add(makeDigitButton("9"));
      buttonPanel.add(makeOperatorButton("/"));
      buttonPanel.add(makeDigitButton("4"));
      buttonPanel.add(makeDigitButton("5"));
      buttonPanel.add(makeDigitButton("6"));
      buttonPanel.add(makeOperatorButton("*"));
      buttonPanel.add(makeDigitButton("1"));
      buttonPanel.add(makeDigitButton("2"));
      buttonPanel.add(makeDigitButton("3"));
      buttonPanel.add(makeOperatorButton("-"));
      buttonPanel.add(makeDigitButton("0"));
      buttonPanel.add(makeDigitButton("."));
      buttonPanel.add(makeOperatorButton("="));
      buttonPanel.add(makeOperatorButton("+"));

      add(buttonPanel, BorderLayout.CENTER);
   }

   class MathOpListener implements ActionListener
   {
      public void actionPerformed(ActionEvent event)
      {  
         double value = Double.parseDouble(display.getText());
         String mathOp = (String) mathOpCombo.getSelectedItem();

         double base = 10;
         if (baseeButton.isSelected()) { base = Math.E; }
         else if (base2Button.isSelected()) { base = 2; }

         boolean radian = radianCheckBox.isSelected();
         if (!radian && (mathOp.equals("sin") 
               || mathOp.equals("cos") || mathOp.equals("tan")))
         {
            value = Math.toRadians(value);
         }

         if (mathOp.equals("sin"))
         {
            value = Math.sin(value);
         }
         else if (mathOp.equals("cos"))
         {
            value = Math.cos(value);
         }
         else if (mathOp.equals("tan"))
         {
            value = Math.tan(value);
         }
         else if (mathOp.equals("log"))
         {
            value = Math.log(value) / Math.log(base);
         }
         else if (mathOp.equals("exp"))
         {
            value = Math.pow(base, value);
         }
         display.setText("" + value);

         startNewValue = true;         
      }
   }

   private JPanel createBaseButtons()
   {
      baseeButton = new JRadioButton("e");
      base10Button = new JRadioButton("10");
      base2Button = new JRadioButton("2");

      baseeButton.setSelected(true);

      ButtonGroup group = new ButtonGroup();
      group.add(baseeButton);
      group.add(base10Button);
      group.add(base2Button);

      JPanel basePanel = new JPanel();
      basePanel.add(baseeButton);
      basePanel.add(base10Button);
      basePanel.add(base2Button);
      basePanel.setBorder(new TitledBorder(new EtchedBorder(), "Base"));
      
      return basePanel;
   }

   private void createControlPanel()
   {
      radianCheckBox = new JCheckBox("Radian");
      radianCheckBox.setSelected(true);

      mathOpCombo = new JComboBox();
      mathOpCombo.addItem("sin");
      mathOpCombo.addItem("cos");
      mathOpCombo.addItem("tan");
      mathOpCombo.addItem("log");
      mathOpCombo.addItem("exp");

      mathOpButton = new JButton("Apply");
      mathOpButton.addActionListener(new MathOpListener());

      JPanel controlPanel = new JPanel();
      controlPanel.add(radianCheckBox);
      controlPanel.add(createBaseButtons());
      controlPanel.add(mathOpCombo);
      controlPanel.add(mathOpButton);

      add(controlPanel, BorderLayout.SOUTH);
   }

   /**
      Combines two values with an operator.
      @param value1 the first value
      @param value2 the second value
      @param op an operator (+, -, *, /, or =)
   */
   public double calculate(double value1, double value2, String op)
   {  
      if (op.equals("+")) 
      {
         return value1 + value2;
      }
      else if (op.equals("-")) 
      {
         return value1 - value2;
      }
      else if (op.equals("*")) 
      {
         return value1 * value2;
      }
      else if (op.equals("/")) 
      {
         return value1 / value2;
      }
      else // "="
      {
         return value2;
      }
   }
   
   class DigitButtonListener implements ActionListener
   {
      private String digit;
      
      /**
         Constructs a listener whose actionPerformed method adds a digit
         to the display.
         @param aDigit the digit to add
      */
      public DigitButtonListener(String aDigit)
      {
         digit = aDigit;
      }
      
      public void actionPerformed(ActionEvent event)
      {  
         if (startNewValue) 
         {
            display.setText("");
            startNewValue = false;
         }
         display.setText(display.getText() + digit);
      }        
   }

   /**
      Makes a button representing a digit of a calculator.
      @param digit the digit of the calculator
      @return the button of the calculator
   */
   public JButton makeDigitButton(String digit)
   {
      JButton button = new JButton(digit);      
      ActionListener listener = new DigitButtonListener(digit);
      button.addActionListener(listener);  
      return button;    
   } 

   class OperatorButtonListener implements ActionListener
   {
      private String operator;

      /**
         Constructs a listener whose actionPerformed method
         schedules an operator for execution.
      */      
      public OperatorButtonListener(String anOperator)
      {
         operator = anOperator;
      }

      public void actionPerformed(ActionEvent event)
      {  
         if (!startNewValue)
         {  
            double value = Double.parseDouble(display.getText());
            lastValue = calculate(lastValue, value, lastOperator);
            display.setText("" + lastValue);
            startNewValue = true;
         }
            
         lastOperator = operator;
      }       
   }

   /**
      Makes a button representing an operator of a calculator.
      @param op the operator of the calculator
      @return the button of the calculator
   */
   public JButton makeOperatorButton(String op)
   {
      JButton button = new JButton(op);      
      ActionListener listener = new OperatorButtonListener(op);
      button.addActionListener(listener);  
      return button;    
   }     
}
