import java.util.*;

/**
 * Provides the basic operations for a four-function,
 * octal/decimal/hexadecimal calculator.
 * 
 * @author David Matuszek
 * @version October 31, 2001
 */
public class CalculatorChip {
    private int firstOperand;
    private int secondOperand;
    private int base = 10;
    private char pendingOperator = '~';
    private boolean isPendingOperator = false;
    private boolean replaceNumberInDisplay = true;
    private boolean isError = false;
    private boolean equalsWasLast = true;
    private int numberInDisplay;
    private char mode;

    /**
     * Starts the display with the digit or appends it to the number
     * already in the display.
     *
     * @param digitChar  a character representing the new digit.
     * @return the string that should be displayed after appending the digit.
     */
    String digitClick(char digitChar) {
        int digit = Character.digit(digitChar, 16);
        if (equalsWasLast) {
            isPendingOperator = false;
        }
        if (digit >= base) {
            isError = true;
            return "Illegal base " + base + " digit: " + digitChar;
        }
        if (replaceNumberInDisplay) {
            secondOperand = digit;
            replaceNumberInDisplay = false;
        }
        else {
            secondOperand = base * secondOperand + digit;
        }
        equalsWasLast = false;
        return makeString(secondOperand);
    }
    
    /**
     * Stores the operator (represented by one of the characters '+', '-',
     * '*', or '/') so that it can be applied later to the operands.
     *
     * @param operator  a character representing the operator to be stored.
     * @return the string that should be displayed after storing the operator.
     */
    String operatorClick(char operator) {
         equalsWasLast = false;
        if (isPendingOperator) {
            if (replaceNumberInDisplay) {
                // previous keystroke was operator or =
                pendingOperator = operator;
                return makeString(firstOperand);
            }
            else { // prior operator, already entering digits
                apply();
                pendingOperator = operator;
                replaceNumberInDisplay = true;
                return makeString(firstOperand);
            }
        }
        else { // no operator pending
            firstOperand = secondOperand;
            secondOperand = 0;
            pendingOperator = operator;
            isPendingOperator = true;
            replaceNumberInDisplay = true;
            return makeString(firstOperand);
        }
    }
    
    /**
     * Handles the equals key.
     *
     * @return the string to be displayed.
     */
    String equalsClick() {
        equalsWasLast = true;
        if (isPendingOperator) {
            if (replaceNumberInDisplay) {
                // previous keystroke was operator or =
                apply();
                return makeString(firstOperand);
            }
            else { // have prior operator, was entering digits
                apply();
                replaceNumberInDisplay = true;
                return makeString(firstOperand);
            }
        }
        else { // no prior operator
            firstOperand = secondOperand;
            return makeString(firstOperand);
        }
    }
    
    /**
     * Sets the calculator to display values in the given base,
     * and returns the new display string. The base must be one
     * of the integers 8, 10, or 16.
     *
     * @param base the base to use, which must be 8, 10, or 16.
     * @return the string to be displayed, representing the number in
     *         the new base.
     */
    String setBase(int base) {
        if (base == 8 || base == 10 || base == 16) {
            this.base = base;
            return makeString(numberInDisplay);
        }
        else {
            return "Illegal base: " + base;
        }
    }
    
    /**
     * Zeros the number in the display, but otherwise leaves
     * the calculator in the same state that it was in.
     *
     * @return either an error message or the string "0".
     */
    String clearEntry() {
        secondOperand = 0;
        return makeString(0);
    }
    
    /**
     * Completely resets the calculator.
     *
     * @return the string "0".
     */
    String clearAll() {
        firstOperand = secondOperand = 0;
        isPendingOperator = false;
        isError = false;
        numberInDisplay = 0;
        return "0";
    }

    /**
     * Returns a string suitable for display, which may be an error message.
     *
     * @param n  the number that should be displayed.
     * @return   a string representing the number to be displayed.
     */
    private String makeString(int n) {
        if (isError) return "Error";
        numberInDisplay = n;
        if (base == 8) {
            return Integer.toOctalString(n);
        }
        else if (base == 10) {
            return n + "";
        }
        else { // base == 16
            return Integer.toHexString(n).toUpperCase();
        }
    }

    /**
     * Computes X = X op Y, where X = this.firstOperand, op = this.pendingOperator,
     * and Y = this.secondOperand.
     */
    private void apply() {
        switch(pendingOperator) {
            case '+':
                firstOperand += secondOperand;
                break;
            case '-':
                firstOperand -= secondOperand;
                break;
            case '*':
                firstOperand *= secondOperand;
                break;
            case '/':
                if (secondOperand == 0) {
                    isError = true;
                }
                else firstOperand /= secondOperand;
                break;
        }
    }
}
