import java.awt.*;

public class Spreadsheet extends Frame {
    protected static int numFields = 16;
    protected Checkbox[] check = new Checkbox[numFields];
    protected CheckboxGroup mutexChecks = new CheckboxGroup();
    protected Label[] field = new Label[numFields];
    protected String[] trueValue = new String[numFields];
    int cursor = 0;
    
    protected TextField editField;
    protected Button docalc;
    protected Button doquit;
    protected int unfocused;
    
    // EvaluateSpreadsheet
    //
    // The function determines which values should be displayed given
    // the formulas in boxes. The values to display go into <result>.
    //
    // <optor>, <lhs_type>, <lhs>, <rhs_type>, and <rhs> encode the
    // formulas. <optor> is 0 for addition, 1 for subtraction, 2 for
    // multiplication, and 3 for division. <lhs_type> is negative if
    // the left operand is a constant, otherwise it is the box to which
    // the left operand refers. <lhs> is the left operand if it is a
    // constant. Similarly, <rhs_type> is negative for a constant right
    // operand, otherwise the box to which the right operand refers,
    // and <rhs> is the right operand.
    //
    // The function places <undefined> into all boxes whose values
    // could not be computed.
    public static void EvaluateSpreadsheet(double[] result, int[] optor,
            int[] lhs_type, double[] lhs, int[] rhs_type, double[] rhs,
            double undefined) {
        int i;

        // There is an error in the homework assignment. The values
        // of boxes 2 and 5 in the example should be -78.6 and -32.2,
        // respectively.
        
        for(i = 0; i < numFields; i++) {
            result[i] = undefined;
        }
    }
    
    // Spreadsheet
    //
    // creates the dialog box
    public Spreadsheet() {
        super("Spreadsheet");

        Panel p;
        GridBagConstraints c;
        GridBagLayout line;
        int i;
        
        unfocused = 0;
        
        // create the grid
        this.setLayout(new GridBagLayout());
        c = new GridBagConstraints();
        c.gridwidth = 1;
        c.gridheight = 1;
        c.ipady = 5;
        c.anchor = GridBagConstraints.CENTER;
        c.weightx = 1.0;
        
        // create the boxes
        for(i = 0; i < numFields; i++) {
            // default values
            trueValue[i] = new String("");
            
            // checkbox for selection
            check[i] = new Checkbox("#" + i);
            check[i].setCheckboxGroup(mutexChecks);
            c.gridx = 0;
            c.gridy = i;
            c.weightx = 0.0;
            c.fill = GridBagConstraints.HORIZONTAL;
            ((GridBagLayout) this.getLayout()).setConstraints(check[i], c);
            this.add(check[i]);
            
            // text field for value
            field[i] = new Label("?");
            c.gridx = 1;
            c.gridy = i;
            c.weightx = 1.0;
            c.fill = GridBagConstraints.HORIZONTAL;
            ((GridBagLayout) this.getLayout()).setConstraints(field[i], c);
            this.add(field[i]);
        }
        
        // initially select the first one
        cursor = 0;
        mutexChecks.setCurrent(check[cursor]);
        
        // create text field for user to edit
        editField = new TextField("");
        c.gridx = 0;
        c.gridy = numFields;
        c.weighty = 1.0;
        c.gridwidth = 2;
        c.fill = GridBagConstraints.HORIZONTAL;
        ((GridBagLayout) this.getLayout()).setConstraints(editField, c);
        this.add(editField);
        
        // create a Panel to hold the buttons.
        p = new Panel();
        p.setLayout(new FlowLayout(FlowLayout.CENTER, 15, 15));
        
        // add the ``Test'' button
        docalc = new Button("Calculate");
        p.add(docalc);
        
        // add the ``Quit'' button
        doquit = new Button("Quit");
        p.add(doquit);
        
        // put Panel in dialog
        c.gridy = numFields + 1;
        c.weighty = 0.0;
        c.fill = GridBagConstraints.NONE;
        ((GridBagLayout) this.getLayout()).setConstraints(p, c);
        this.add(p);
        this.pack();
    }
    
    // action
    //
    // handle button clicks
    public boolean action(Event e, Object arg) {
        int i;

        if(e.target == docalc || e.target == editField) {
            // recalculate values
            trueValue[cursor] = editField.getText();
            CalculateSheet();
            return true;
        } else if(e.target == doquit) {
            // quit
            this.dispose();
            return true;
        } else {
            return false;
        }
    }
    
    // CalculateSheet
    //
    // This function gets all the formulas, interprets them, calculates
    // the values to display (using EvaluateSpreadsheet), and finally
    // displays them.
    public void CalculateSheet() {
        double[] result = new double[numFields];
        int[] optor = new int[numFields];
        int[] lhs_type = new int[numFields];
        double[] lhs = new double[numFields];
        int[] rhs_type = new int[numFields];
        double[] rhs = new double[numFields];
        double undefined = Double.MAX_VALUE;
        int i;
        int j;
        int begin_num;
        
        // interpret formulas into arrays
        for(i = 0; i < numFields; i++) {
            int[] op = new int[1];
            int[] x_type = new int[1];
            double[] x = new double[1];
            int[] y_type = new int[1];
            double[] y = new double[1];
            boolean ok;

            op[0] = 0;
            x_type[0] = -1;
            x[0] = undefined;
            y_type[0] = -1;
            y[0] = 0;

            ok = InterpretFormula(trueValue[i], op, x_type, x, y_type,
                y);
            if(ok) {
                optor[i] = op[0];
                lhs_type[i] = x_type[0];
                lhs[i] = x[0];
                rhs_type[i] = y_type[0];
                rhs[i] = y[0];
            } else {
                lhs_type[i] = -1;
                lhs[i] = undefined;
                optor[i] = 0;
                rhs_type[i] = -1;
                rhs[i] = 0;
            }
            
        }
        
        // Determine spreadsheet values
        EvaluateSpreadsheet(result, optor, lhs_type, lhs, rhs_type, rhs, undefined);
        
        // Display values
        for(i = 0; i < numFields; i++) {
            if(result[i] == undefined) {
                field[i].setText("?");
            } else {
                field[i].setText(Double.toString(result[i]));
            }
        }
    }   

    // InterpretFormula
    //
    // This method interprets the string <str> as a formula, putting
    // the interpretation into its parameters in the format accepted
    // by EvaluateSpreadsheet. It returns true on success, false if
    // the formula could not be interpreted.
    public static boolean InterpretFormula(String str, int[] op,
            int[] x_type, double[] x, int[] y_type, double[] y) {
        int j;
        int begin_num;

        j = 0;
        
        // skip white space
        while(j < str.length() && Character.isSpace(str.charAt(j))) j++;
        
        // get first operator
        if(j < str.length() && str.charAt(j) == '#') {
            x_type[0] = 0;
            j++;
        } else {
            x_type[0] = -1;
        }
        
        if(j == str.length()) {
            return false;
        }
        
        begin_num = j;
        if(j < str.length() && str.charAt(j) == '-') ++j;
        while(j < str.length() && (Character.isDigit(str.charAt(j)) || str.charAt(j) == '.')) {
            j++;
        }
        try {
            if(x_type[0] < 0) {
                x[0] = Double.valueOf(str.substring(begin_num, j)).doubleValue();
            } else {
                x_type[0] = Integer.valueOf(str.substring(begin_num, j)).intValue();
            }
        } catch(NumberFormatException e) {
            return false;
        }
        
        // skip white space
        while(j < str.length() && Character.isSpace(str.charAt(j))) j++;
        if(j == str.length()) {
            op[0] = 0;
            y_type[0] = -1;
            y[0] = 0;
            return true;
        }
        
        // get operator
        if(str.charAt(j) == '+') {
            op[0] = 0;
        } else if(str.charAt(j) == '-') {
            op[0] = 1;
        } else if(str.charAt(j) == '*') {
            op[0] = 2;
        } else if(str.charAt(j) == '/') {
            op[0] = 3;
        } else {
            return false;
        }
        j++;
        
        // skip white space
        while(j < str.length() && Character.isSpace(str.charAt(j))) j++;
        if(j == str.length()) {
            return false;
        }
        
        // get second operator
        if(j< str.length() && str.charAt(j) == '#') {
            y_type[0] = 0;
            j++;
        } else {
            y_type[0] = -1;
        }
        
        if(j == str.length()) {
            return false;
        }
        
        begin_num = j;
        if(j < str.length() && str.charAt(j) == '-') ++j;
        while(j < str.length() && (Character.isDigit(str.charAt(j)) || str.charAt(j) == '.')) {
            j++;
        }
        try {
            if(y_type[0] < 0) {
                y[0] = Double.valueOf(str.substring(begin_num, j)).doubleValue();
            } else {
                y_type[0] = Integer.valueOf(str.substring(begin_num, j)).intValue();
            }
        } catch(NumberFormatException e) {
            return false;
        }

        // skip whate space
        while(j < str.length() && Character.isSpace(str.charAt(j))) j++;
        if(j != str.length()) {
            return false;
        }

        return true;
    }
    
    // gotFocus
    //
    // sets the default focus location
    public boolean gotFocus(Event e, Object arg) {
        int i;
        
        for(i = 0; i < numFields; i++) {
            if(e.target == field[i] || e.target == check[i]) {
                // The user has selected another box, so change the
                // current values to reflect this.
                trueValue[cursor] = editField.getText();
                
                cursor = i;
                mutexChecks.setCurrent(check[cursor]);
                editField.setText(trueValue[cursor]);
                editField.requestFocus();
                return true;
            }
        }
        return true;
    }
    
    // main
    //
    // this is where the program begins
    public static void main(String[] args) {
        Frame f;
        Spreadsheet dialog;
        
        // create the dialog box
        f = new Spreadsheet();
        f.resize(300, 500);
        f.show();
    }
}
