//++
//
//  File:   Calculator.java
//
//  Description:
//
//      This file contains the classes required to implement
//      an applet to hold the demonstration Calculator.
//
//
//  MODULE HISTORY
//
//  Version Date        Author          Comments
//                                      
//  1.0     02 Aug 1996 A J Brabban     Original
//
//--

import java.applet.*;
import java.awt.*;
import java.lang.Math;
import AppletFrame;

//+
//
//  Class:          Calculator
//  
//  Super-Class:    Applet
//
//  Interfaces:     None
//
//  Description:    This class implements an applet to hold the
//                  simple calculator.
//
//  Hungarian Tag:  clc
//
//-
public class Calculator extends Applet
{
    //+
    //  Instance Data
    //-
    boolean         m_fStandAlone = false;

    TextField       m_txtUserInput;
    boolean         m_fUserEntered = true;
    boolean         m_fDecimalPointEntered = false;
    NumberKeyPad    m_nkpNumberKeyPad;
    OperationsPad   m_oppOperationsPad;
    MemoryPad       m_mypMemoryPad;
    Label           m_lblMemoryIndicator;
    double          m_dOperandFirst = 0;
    double          m_dMemory;
    boolean         m_fMemoryInUse = false;
    String          m_sOperation = new String("");

    //+
    //
    //  Method:             main
    //
    //  Description:
    //      This method is provided to allow the calculator to be
    //      started as an application.
    //
    //  Parameters:
    //      args:
    //      Type array of String, the command line arguments.
    //
    //  Throws:             none
    //          
    //  Global Vars Used:   none
    //
    //  Class Vars Used:    none
    //
    //  Instance Vars Used: none
    //
    //-
    public static void main(String args[])
    {
        //+
        //  Create a top level Window to put the applet into.
        //-
        AppletFrame apfFrame = new AppletFrame();
        apfFrame.setTitle("Calculator");

        //+
        // The following code starts the applet running within the frame window.
        //-
        Calculator clcCalculator = new Calculator();

        apfFrame.add("Center", clcCalculator);
        clcCalculator.m_fStandAlone = true;
        clcCalculator.init();
        clcCalculator.start();
        apfFrame.pack();
        apfFrame.show();
    }

    //+
    //
    //  Method:             getAppletInfo
    //
    //  Description:
    //      This method returns a string describing the applet's
    //      author, copyright date, or miscellaneous information.
    //
    //  Parameters:         none
    //
    //  Throws:             none
    //          
    //  Global Vars Used:   none
    //
    //  Class Vars Used:    none
    //
    //  Instance Vars Used: none
    //
    //-
    public String getAppletInfo()
    {
        return "Name:   Calculator\r\n" +
               "Author: Andrew Brabban\r\n" +
               "Created with Microsoft Visual J++ Version 1.0";
    }


    //+
    //
    //  Method:             init
    //
    //  Description:
    //      Create all the components required for the calculator.
    //      They are arranged using the GridBagLayout layout manager.
    //
    //  Parameters:         none
    //
    //  Throws:             none
    //          
    //  Global Vars Used:   none
    //
    //  Class Vars Used:    none
    //
    //  Instance Vars Used:
    //      m_lblMemoryIndicator:
    //          A label object is created to display if the
    //          memory is in use.
    //
    //      m_txtUserInput:
    //          A TextField object is created to display numbers
    //          the user enters and results.
    //
    //      m_mypMemoryPad:
    //          A MemoryPad object is created to supply buttons
    //          for the memory operations.
    //
    //      m_nkpNumberKeyPad:
    //          A NumberKeyPad object is created to supply buttons
    //          for inputting numbers.
    //
    //      m_oppOperationsPad:
    //          An OperationsPad object is created to supply
    //          buttons for the mathematical operations supported.
    //
    //-
    public void init()
    {
        GridBagLayout       layGridBag          = new GridBagLayout();
        GridBagConstraints  conGridBag          = new GridBagConstraints();
        Font                fonUserInputStyle   = new Font("Courier", Font.BOLD, 14);
        
        //+
        //  Set the layout to Grid Bag
        //-
        setLayout(layGridBag);

        //+
        //  Add a text field to hold the memory in use indicator.
        //-
        m_lblMemoryIndicator    = new Label("  ");
        conGridBag.fill         = GridBagConstraints.NONE;
        conGridBag.anchor       = GridBagConstraints.WEST;
        conGridBag.gridwidth    = GridBagConstraints.RELATIVE;
        conGridBag.ipadx        = 5;
        conGridBag.ipady        = 5;
        conGridBag.insets       = new Insets(5,5,5,5);
        layGridBag.setConstraints(m_lblMemoryIndicator, conGridBag);
        add(m_lblMemoryIndicator);

        //+
        //  Add a text field to display numbers as they are entered.
        //-
        m_txtUserInput = new TextField(String.valueOf(m_dOperandFirst), 12);
        m_txtUserInput.setFont(fonUserInputStyle);
        m_fUserEntered = false;

        conGridBag.anchor       = GridBagConstraints.EAST;
        conGridBag.gridwidth    = GridBagConstraints.REMAINDER;
        layGridBag.setConstraints(m_txtUserInput, conGridBag);

        add(m_txtUserInput);

        //+
        //  Add the memory operations pad.
        //-
        m_mypMemoryPad = new MemoryPad();

        conGridBag.fill         = GridBagConstraints.BOTH;
        conGridBag.anchor       = GridBagConstraints.CENTER;
        conGridBag.weightx      = 1.0;
        conGridBag.weighty      = 1.0;
        conGridBag.gridwidth    = GridBagConstraints.REMAINDER;
        layGridBag.setConstraints(m_mypMemoryPad, conGridBag);
        add(m_mypMemoryPad);
        
        //+
        //  Add a number key pad to the calculator.
        //-
        m_nkpNumberKeyPad = new NumberKeyPad();

        conGridBag.weightx      = 10.0;
        conGridBag.weighty      = 20.0;
        conGridBag.gridwidth    = GridBagConstraints.RELATIVE;
        layGridBag.setConstraints(m_nkpNumberKeyPad, conGridBag);
        add(m_nkpNumberKeyPad);

        //+
        //  Create a panel for the operations allowed.
        //-
        m_oppOperationsPad = new OperationsPad();

        conGridBag.weightx      = 1.0;
        conGridBag.gridwidth    = GridBagConstraints.REMAINDER;
        layGridBag.setConstraints(m_oppOperationsPad, conGridBag);
        add(m_oppOperationsPad);

    } // End init

    //+
    //
    //  Method:             action
    //
    //  Description:
    //      This method overrides the method inherited from
    //      Component. It is called whenever an action occurs in the
    //      calculator. It handles the user pressing any of the
    //      buttons on the calculator.
    //
    //  Parameters:
    //      event
    //          Type Event, the event that caused the action
    //      
    //      what
    //          Type Object, the action which occured. What type of
    //          object this is depends upon what type of event
    //          caused the action.
    //          
    //  Throws:             none
    //          
    //  Global Vars Used:   none
    //
    //  Class Vars Used:    none
    //
    //  Instance Vars Used:
    //      m_nkpNumberKeyPad:
    //          Used to check if a button is on the number key pad.
    //
    //      m_oppOperationsPad:
    //          Used to check if a button is on the operations pad.
    //
    //      m_mypMemoryPad:
    //          Used to check if a button is on the memory ops pad.
    //
    //      m_fUserEntered:
    //          The flag indicating if the number currently
    //          displayed is user entered is checked and set.
    //
    //      m_fDecimalPointEntered:
    //          The flag indicating if a decimal point has been
    //          entered is checked and set.
    //
    //      m_txtUserInput:
    //          User input and results are placed in the display.
    //
    //-
    public boolean action(Event event, Object what)
    {
        //+
        //  Get the object in which the action event occured.
        //-
        Object objTarget = event.target;

        //+
        //  Has a button been pressed?
        //-
        if (objTarget.getClass().getName().equals("java.awt.Button"))
        {
            //+
            //  Get the button pressed.
            //-
            Button butPressed = (Button)objTarget;

            //+
            //  Is this a button on the number key pad?
            //-
            if (butPressed.getParent() == m_nkpNumberKeyPad)
            {
                //+
                //  If we have a number key the user entered then append this
                //  digit.
                //
                if (m_fUserEntered)
                {
                    //+
                    //  Ignore a decimal point if we already have one
                    //-
                    if (".".equals(what))
                    {
                        if (m_fDecimalPointEntered)
                        {
                            return true;
                        }
                        else
                        {
                            //+
                            //  Note that we have a decimal point.
                            //-
                            m_fDecimalPointEntered = true;
                        }
                    }

                    //+
                    //  Append the number/decimal point entered.
                    //-
                    m_txtUserInput.setText(m_txtUserInput.getText() + (String)what);
                }
                else
                {
                    //+
                    //  Note any decimal point entered.
                    //-
                    if (".".equals(what))
                    {
                        m_fDecimalPointEntered = true;
                    }

                    //+
                    //  Set the user input and note that this is
                    //  a user inputed number.
                    //-
                    m_txtUserInput.setText((String)what);
                    m_fUserEntered = true;
                }

                //+
                //  Return true to indicate that we have handled
                //  the action.
                //-
                return true;
            }
            //+
            //  ... is this an operations button?
            //-
            else if (butPressed.getParent() == m_oppOperationsPad)
            {
                if (OperationsPad.opp_sCLEAR_ENTRY.equals(what))
                {
                    //+
                    //  Clear Entry. Set the number to an non-user
                    //  entered 0.
                    //-
                    m_txtUserInput.setText("0");
                    m_fUserEntered = false;
                    m_fDecimalPointEntered = false;
                    
                    return true;
                }
                else if (OperationsPad.opp_sCLEAR.equals(what))
                {
                    //+
                    //  Clear. Set the number to an non-user
                    //  entered 0 and clear any stored operation.
                    //-
                    m_txtUserInput.setText("0");
                    m_fUserEntered = false;
                    m_fDecimalPointEntered = false;
                    m_sOperation = new String("");

                    return true;
                }
                else if (OperationsPad.opp_sBACKSPACE.equals(what))
                {
                    //+
                    //  Backspace. Delete the last entered digit.
                    //-
                    int nInputLength = m_txtUserInput.getText().length();

                    if ((nInputLength > 0) && m_fUserEntered)
                    {
                        //+
                        //  Clear the decimal point flag if we delete
                        //  a decimal point.
                        //-
                        if (m_txtUserInput.getText().charAt(nInputLength - 1) == '.')
                        {
                            m_fDecimalPointEntered = false;
                        }

                        m_txtUserInput.setText(m_txtUserInput.getText().substring(0, nInputLength - 1));

                        if (nInputLength == 1)
                        {
                            m_txtUserInput.setText("0");
                            m_fUserEntered = false;
                        }
                    }

                    return true;
                }
                else if (   (OperationsPad.opp_sPLUS.equals(what))
                         || (OperationsPad.opp_sMINUS.equals(what))
                         || (OperationsPad.opp_sMULTIPLY.equals(what))
                         || (OperationsPad.opp_sDIVIDE.equals(what))
                        )
                {
                    //+
                    //  Store the operation and the operand.
                    //-
                    m_sOperation = new String((String)what);
                    m_dOperandFirst = (Double.valueOf(m_txtUserInput.getText())).doubleValue();

                    //+
                    //  Clear the number field
                    //-
                    m_fUserEntered = false;
                    m_fDecimalPointEntered = false;
                    m_txtUserInput.setText("0");

                    return true;
                }
                else if (OperationsPad.opp_sEQUALS.equals(what))
                {
                    //+
                    //  Equals. Perform the operation.
                    //-
                    double   dResult;
                    double   dOperandSecond = (Double.valueOf(m_txtUserInput.getText())).doubleValue();

                    if (m_sOperation.equals(OperationsPad.opp_sPLUS))
                    {
                        dResult = m_dOperandFirst + dOperandSecond;
                    }
                    else if (m_sOperation.equals(OperationsPad.opp_sMINUS))
                    {
                        dResult = m_dOperandFirst - dOperandSecond;
                    }
                    else if (m_sOperation.equals(OperationsPad.opp_sMULTIPLY))
                    {
                        dResult = m_dOperandFirst * dOperandSecond;
                    }
                    else if (m_sOperation.equals(OperationsPad.opp_sDIVIDE))
                    {
                        dResult = m_dOperandFirst / dOperandSecond;
                    }
                    else
                    {
                        dResult = dOperandSecond;
                    }

                    //+
                    //  Check for errors and display an error or the result
                    //  as appropriate.
                    //-
                    if (Double.isNaN(dResult) || Double.isInfinite(dResult))
                    {
                        m_txtUserInput.setText("Error");
                        m_dOperandFirst = 0;
                    }
                    else
                    {
                        m_txtUserInput.setText(String.valueOf(dResult));
                        m_dOperandFirst = dResult;
                    }
                    m_sOperation = new String("");
                    m_fDecimalPointEntered = false;
                    m_fUserEntered = false;

                    return true;
                }
                else if (OperationsPad.opp_sSQUARE.equals(what))
                {
                    //+
                    //  Square.
                    //-
                    double  dOperand = (Double.valueOf(m_txtUserInput.getText())).doubleValue();
                    double  dResult = java.lang.Math.pow(dOperand, 2);

                    //+
                    //  Check for errors and display error or result
                    //  as appropriate.
                    //-
                    if (Double.isNaN(dResult) || Double.isInfinite(dResult))
                    {
                        m_txtUserInput.setText("Error");
                        m_dOperandFirst = 0;
                    }
                    else
                    {
                        m_txtUserInput.setText(String.valueOf(dResult));
                        m_dOperandFirst = dResult;
                    }
                    m_sOperation = new String("");
                    m_fDecimalPointEntered = false;
                    m_fUserEntered = false;

                    return true;
                }
                else if (OperationsPad.opp_sSQUARE_ROOT.equals(what))
                {
                    //+
                    //  Square Roor.
                    //-
                    double dOperand = (Double.valueOf(m_txtUserInput.getText())).doubleValue();
                    double dResult = java.lang.Math.sqrt(dOperand);

                    //+
                    //  Check for errors and display error or result
                    //  as appropriate.
                    //-
                    if (Double.isNaN(dResult) || Double.isInfinite(dResult))
                    {
                        m_txtUserInput.setText("Error");
                        m_dOperandFirst = 0;
                    }
                    else
                    {
                        m_txtUserInput.setText(String.valueOf(dResult));
                        m_dOperandFirst = dResult;
                    }
                    m_sOperation = new String("");
                    m_fDecimalPointEntered = false;
                    m_fUserEntered = false;

                    return true;
                }
            }
            //+
            //  ... is this a memory operation button?
            //-
            else if (butPressed.getParent() == m_mypMemoryPad)
            {
                if (m_mypMemoryPad.myp_sCLEAR.equals(what))
                {
                    //+
                    //  Memory Clear. Clear the memory and the
                    //  indicator.
                    //-
                    m_lblMemoryIndicator.setText("  ");
                    m_dMemory = 0.0;
                    m_fMemoryInUse = false;

                    return true;
                }
                else if (m_mypMemoryPad.myp_sSET.equals(what))
                {
                    //+
                    //  Memory Set. Set the memory to the current
                    //  input number and set the indicator.
                    //-
                    m_lblMemoryIndicator.setText("M");
                    m_fMemoryInUse = true;
                    m_dMemory = (Double.valueOf(m_txtUserInput.getText())).doubleValue();

                    return true;
                }
                else if (m_mypMemoryPad.myp_sRECALL.equals(what))
                {
                    //+
                    //  Memory Recall. Set the input to the value in
                    //  memory if the memory is in use.
                    //-
                    if (m_fMemoryInUse)
                    {
                        m_txtUserInput.setText(String.valueOf(m_dMemory));
                        m_fUserEntered = false;
                        m_fDecimalPointEntered = false;
                    }
                    
                    return true;
                }
                else if (m_mypMemoryPad.myp_sPLUS.equals(what))
                {
                    //+
                    //  Memory Plus. Add the current input number to
                    //  what is stored in memory.
                    //-
                    if (m_fMemoryInUse)
                    {
                        m_dMemory += (Double.valueOf(m_txtUserInput.getText())).doubleValue();
                    }
                    else
                    {
                        m_lblMemoryIndicator.setText("M");
                        m_fMemoryInUse = true;
                        m_dMemory = (Double.valueOf(m_txtUserInput.getText())).doubleValue();
                    }

                    return true;
                }
                else if (m_mypMemoryPad.myp_sMINUS.equals(what))
                {
                    //+
                    //  Memory Minus. Subtract the current input
                    //  number to what is stored in memory.
                    //-
                    if (m_fMemoryInUse)
                    {
                        m_dMemory -= (Double.valueOf(m_txtUserInput.getText())).doubleValue();
                    }
                    else
                    {
                        m_lblMemoryIndicator.setText("M");
                        m_fMemoryInUse = true;
                        m_dMemory = -(Double.valueOf(m_txtUserInput.getText())).doubleValue();
                    }

                    return true;
                }
            }
        }

        //+
        //  If this is an event we do not know how to process then
        //  there is a programming error. Display the event to help
        //  debugging.
        //-
        System.out.println("Event:");
        System.out.println(event.toString());
        System.out.println("What (" +what.getClass().getName() + "):");
        System.out.println(what.toString());

        return false;

    } // End action

} // End class Calculator

//+
//
//  Class:          NumberKeyPad
//  
//  Super-Class:    Panel
//
//  Interfaces:     None
//
//  Description:    This class implements a panel of buttons which
//                  make up a key pad of numbers. The number keys
//                  are held in a Panel and arranged using the Grid
//                  layout.
//
//  Hungarian Tag:  nkp
//
//-
class NumberKeyPad extends Panel
{
    //+
    //  Class Data
    //-
    static final int    m_cNUMBER_KEYS_MAX = 11;

    //+
    //  Instance Data
    //-
    Button[] m_rgbutNumbers;

    //+
    //
    //  Method:             NumberKeyPad
    //
    //  Description:
    //      This method is the constructor for the NumberKeyPad
    //      class. All compoment objects are created.
    //
    //  Parameters:         none
    //
    //  Throws:             none
    //          
    //  Global Vars Used:   none
    //
    //  Class Vars Used:    none
    //
    //  Instance Vars Used:
    //      m_rgbutNumbers:
    //          An array of buttons for the number buttons
    //          created.
    //
    //-
    public NumberKeyPad ()
    {
        //+
        //  Borrow the super-class constructor.
        //-
        super();
        
        //+
        //  Set the layout to 3 columns with a gap between numbers
        //-
        setLayout(new GridLayout(0, 3, 5, 5));

        //+
        //  Add all the buttons
        //-
        m_rgbutNumbers = new Button[m_cNUMBER_KEYS_MAX];

        //+
        //  Add the digits 0 to 9 in the usual key pad arrangement,
        //-
        for (int iButton = 0; iButton <= 9; iButton++)
        {
            m_rgbutNumbers[iButton] = new Button("" + iButton);
            add(m_rgbutNumbers[iButton], iButton == 0 ? -1 : (iButton - 1) % 3);
        }

        //+
        //  Add the decimal point
        //-
        m_rgbutNumbers[10] = new Button (".");
        add(m_rgbutNumbers[10]);

    } // End NumberKeyPad

} // End class NumberKeyPad

//+
//
//  Class:          OperationsPad
//  
//  Super-Class:    Panel
//
//  Interfaces:     None
//
//  Description:    This class implements a panel of buttons which
//                  make up a key pad of operations. The keys are
//                  held in a Panel and arranged using the Grid
//                  layout.
//
//  Hungarian Tag:  opp
//
//-
class OperationsPad extends Panel
{
    //+
    //  Class Data
    //-
    public static final String  opp_sEQUALS         = new String("=");
    public static final String  opp_sPLUS           = new String("+");
    public static final String  opp_sMINUS          = new String("-");
    public static final String  opp_sMULTIPLY       = new String("*");
    public static final String  opp_sDIVIDE         = new String("/");
    public static final String  opp_sCLEAR_ENTRY    = new String("CE");
    public static final String  opp_sCLEAR          = new String("C");
    public static final String  opp_sBACKSPACE      = new String("Del");
    public static final String  opp_sSQUARE         = new String("Sqr");
    public static final String  opp_sSQUARE_ROOT    = new String("Sqrt");

    static final        int     m_cOPERATIONS_MAX = 10;
    
    //+
    //  Instance Data
    //-
    Button[] m_rgbutOperations;

    //+
    //
    //  Method:             OperationsPad
    //
    //  Description:
    //      This method is the constructor for the OperationsPad
    //      class. All compoment objects are created.
    //
    //  Parameters:         none
    //
    //  Throws:             none
    //          
    //  Global Vars Used:   none
    //
    //  Class Vars Used:    none
    //
    //  Instance Vars Used:
    //      m_rgbutOperations
    //          An array of buttons for the opertions buttons
    //          created.
    //
    //-
    public OperationsPad ()
    {
        //+
        //  Borrow the super-class constructor.
        //-
        super();
        
        //+
        //  Set the layout to 2 columns with a gap between numbers
        //-
        setLayout(new GridLayout(0, 2, 5, 5));

        //+
        //  Add all the buttons
        //-
        m_rgbutOperations = new Button[m_cOPERATIONS_MAX];

        int iButton = 0;
        m_rgbutOperations[iButton++] = new Button(opp_sPLUS);
        m_rgbutOperations[iButton++] = new Button(opp_sMINUS);
        m_rgbutOperations[iButton++] = new Button(opp_sMULTIPLY);
        m_rgbutOperations[iButton++] = new Button(opp_sDIVIDE);
        m_rgbutOperations[iButton++] = new Button(opp_sEQUALS);
        m_rgbutOperations[iButton++] = new Button(opp_sBACKSPACE);
        m_rgbutOperations[iButton++] = new Button(opp_sSQUARE);
        m_rgbutOperations[iButton++] = new Button(opp_sSQUARE_ROOT);
        m_rgbutOperations[iButton++] = new Button(opp_sCLEAR_ENTRY);
        m_rgbutOperations[iButton++] = new Button(opp_sCLEAR);

        for (iButton = 0; iButton < m_rgbutOperations.length; iButton++)
        {
            add(m_rgbutOperations[iButton]);
        }

    } // End OperationsPad

} // End class OperationsPad

//+
//
//  Class:          MemoryPad
//  
//  Super-Class:    Panel
//
//  Interfaces:     None
//
//  Description:    This class implements a panel of buttons for
//                  memory operations. The number keys are held in
//                  a Panel and arranged using the Grid layout.
//
//  Hungarian Tag:  myp
//
//-
class MemoryPad extends Panel
{
    //+
    //  Class Data
    //-
    public static final String  myp_sSET    = new String("MS");
    public static final String  myp_sPLUS   = new String("M+");
    public static final String  myp_sMINUS  = new String("M-");
    public static final String  myp_sCLEAR  = new String("MC");
    public static final String  myp_sRECALL = new String("MR");

    static final        int     m_cMEMORY_OPS_MAX = 5;
    
    //+
    //  Instance Data
    //-
    Button[] m_rgbutMemory;

    //+
    //
    //  Method:             MemoryPad
    //
    //  Description:
    //      This method is the constructor for the MemoryPad
    //      class. All compoment objects are created.
    //
    //  Parameters:         none
    //
    //  Throws:             none
    //          
    //  Global Vars Used:   none
    //
    //  Class Vars Used:    none
    //
    //  Instance Vars Used:
    //      m_rgbutMemory
    //          An array of buttons for the memory opertions buttons
    //          created.
    //
    //-
    public MemoryPad ()
    {
        //+
        //  Borrow the super-class constructor.
        //-
        super();
        
        //+
        //  Set the layout to 1 row with a gap between numbers
        //-
        setLayout(new GridLayout(1, 0, 5, 5));

        //+
        //  Add all the buttons.
        //-
        m_rgbutMemory = new Button[m_cMEMORY_OPS_MAX];

        int iButton = 0;
        m_rgbutMemory[iButton++] = new Button(myp_sPLUS);
        m_rgbutMemory[iButton++] = new Button(myp_sMINUS);
        m_rgbutMemory[iButton++] = new Button(myp_sSET);
        m_rgbutMemory[iButton++] = new Button(myp_sCLEAR);
        m_rgbutMemory[iButton++] = new Button(myp_sRECALL);

        for (iButton = 0; iButton < m_rgbutMemory.length; iButton++)
        {
            add(m_rgbutMemory[iButton]);
        }

    } // End MemoryPad

} // End class MemoryPad
