import java.awt.*;
import java.awt.event.*;
/**
 * A GUI for testing the Fraction Class.
 *<P>
 * Provides input widgets for 2 numerators and denominators,
 * and performs all supported operations on the Fraction Class
 * when a "Go" button is clicked.
 *
 * @author Clif Flynt
 * @author Samuel A. Rebelsky
 * @version 1.0
 */

public class FractionTest
    extends WindowAdapter
    implements ActionListener 
{

    // +--------+-----------------------------------------
    // | Fields |
    // +--------+

    /** The primary frame for this GUI  */
    protected Frame ftFrame;

    /** the first fraction to be created from user inputs. */
    protected Fraction fraction1;

    /** the other  fraction to be created from user inputs. */
    protected Fraction fraction2;

    /** The TextField to input the numerator for fraction1 */
    protected TextField num1;

    /** The TextField to input the denominator for fraction2 */
    protected TextField num2;

    /** The TextField to input the numerator for fraction2 */
    protected TextField den1;

    /** The TextField to input the denominator for fraction2 */
    protected TextField den2;

    // +-------------------------------+------------------
    // | Labels for presenting results |
    // +-------------------------------+

    private Label add;
    private Label subtract;
    private Label multiply;
    private Label divide;
    private Label equals;
    private Label equalsObj;

    private Label add_int;
    private Label subtract_int;
    private Label multiply_int;
    private Label divide_int;
    private Label equals_int;
    private Label equalsObj_int;

    // +---------+----------------------------------------
    // | Buttons |
    // +---------+

    /** A button to run the test with currently entered numbers. */
    private Button go;

    /** A button to stop the current task. */
    private Button quit;

    /** A button to reveal a brief help window. */
    private Button help;
    
    /** A button to  delete the contents of the entry windows and results. */
    private Button reset;

    // +-------------+------------------------------------
    // | Contructors |
    // +-------------+

    /**
     * Constructor for the FractionTest GUI.<BR>
     * No arguments.<BR>
     * Builds a primary frame, 2 panels, buttons, labels and TextFields<BR>
     *   one panel for the input labels & TextFields and the buttons.<BR>
     *   one panel for the results and info labels.<BR>
     */

    public FractionTest () {

	// Define fonts for the results and the headings.

	Font result = new Font("helvetica", Font.PLAIN, 12);
	Font heading = new Font(result.getName(), Font.BOLD, result.getSize());

	// Build the primary frame, and set the default font to the
	// Bold heading font.
	
        ftFrame = new Frame("FractionTester");
	ftFrame.setFont(heading);

	// I tried using a separate panel for buttons, but
	// discarded this for aesthetic reasons.  I leave
	// the code in the file (but commented out) for
	// easy conversion to that layout for future comparisons.

	// Panel buttons = new Panel();

	// Build panels for the inputs and results.
	// Having separate panels makes the display look better.
	//
	// The grid layout finds a size that will fit the largest
	// item being displayed, and uses that size for all cells
	// in the grid.
	//
	// There are a different number of rows for input/buttons
	// than there are for results & headings, which also makes using
	// two panels work better.


	Panel inputs = new Panel();
	Panel results = new Panel();

	// Borderlayout is good for the Primary frame -
	// When I was testing the 3 panel version with a separate button
	// panel I set the panels to add North, East and West.
	//
	// With just two panels, FlowLayout could be used, but that
	// would make it harder to shift back and forth between the
	// two and three panel display.

        ftFrame.setLayout(new BorderLayout());
	inputs.setLayout(new GridLayout(6,2));
	results.setLayout(new GridLayout(8,3));
	// buttons.setLayout(new GridLayout(1,2));

	ftFrame.add(inputs, "West");
	ftFrame.add(results, "East");
	// ftFrame.add(buttons, "North");

	ftFrame.addWindowListener(this);

	// The input fields for the numerators and denominators
	num1 = new TextField();
	den1 = new TextField();
	num2 = new TextField();
	den2 = new TextField();

	// Outputs for results of two Fractions being operated on

	add =       new Label();
	subtract =  new Label();
	multiply =  new Label();
	divide =    new Label();
	equals =    new Label();
	equalsObj = new Label();

	// Outputs for results of a Fraction being operated on by an int.

	add_int =       new Label();
	subtract_int =  new Label();
	multiply_int =  new Label();
	divide_int =    new Label();
	equals_int =    new Label();
	equalsObj_int = new Label();

	// The four buttons, and assign their actionListeners

	go    = new Button("Go");
	help  = new Button("Help");
	quit  = new Button("Quit");
	reset = new Button("Reset");

	quit.addActionListener(this);
	go.addActionListener(this);
	help.addActionListener(this);
	reset.addActionListener(this);

	// Add the input and results widgets, in the order
	// that they need to go to fill the GridLayout manager.

	// Using the "buttons" Panel puts the buttons across
	// the top of the GUI.
	// This is aesthetically displeasing to my eye, since the
	// top buttons look too much like column headings.

	//		buttons.add(go);
	//		buttons.add(quit);
	//		buttons.add(help);
	//		buttons.add(reset);

	inputs.add(go);
	inputs.add(quit);
	inputs.add(help);
	inputs.add(reset);

	inputs.add(new Label("Numerator 1"));
	inputs.add(num1);
	inputs.add(new Label("Denominator 1"));
	inputs.add(den1);
	inputs.add(new Label("Numerator 2"));
	inputs.add(num2);
	inputs.add(new Label("Denominator 2"));
	inputs.add(den2);

	// Lets make the results pane a lighter shade of pale
	// so it will stand out a bit.

	results.setBackground(new Color(240, 240, 240));

	// The column labels.

	results.add(new Label("Operation"));
	results.add(new Label("Fraction1 op"));
	results.add(new Label("Fraction1 op"));
	results.add(new Label("performed"));
	results.add(new Label("Fraction2"));
	results.add(new Label("Numerator2"));

	// And the row labels and results

	results.add(new Label("add"));
	results.add(add);
	results.add(add_int);
	results.add(new Label("subtract"));
	results.add(subtract);
	results.add(subtract_int);
	results.add(new Label("multiply"));
	results.add(multiply);
	results.add(multiply_int);
	results.add(new Label("divide"));
	results.add(divide);
	results.add(divide_int);
	results.add(new Label("equals"));
	results.add(equals);
	results.add(equals_int);
	results.add(new Label("equals-Label"));
	results.add(equalsObj);
	results.add(equalsObj_int);

	// Set the Labels that will display results to the
	// regular (not bold) font.

	add.setFont(result);
	add_int.setFont(result);
	subtract.setFont(result);
	subtract_int.setFont(result);
	multiply.setFont(result);
	multiply_int.setFont(result);
	divide.setFont(result);
	divide_int.setFont(result);
	equals.setFont(result);
	equals_int.setFont(result);
	equalsObj.setFont(result);
	equalsObj_int.setFont(result);

	// Finally, pack it, show it, and move it to the corner of the
	// screen.
	//
	// Using FVWM the window tried to place itself along the bottom
	// of the screen, and displayed mostly off the screen.
	//
	// I believe the window was placing itself before it set its size,
	// so FVWM found a place where there was room for a 0 sized window.

	ftFrame.pack();
	ftFrame.show();
	ftFrame.setLocation(50, 50);
    }

    private void doTestsAndUpdates() {
	Fraction ans;
	long n1, n2, d1, d2;

	    // There may be illegal values (non-integers) in the
	    // input TextFields.
	    //
	    // pos will be used to track how far the data parsing
	    // has progressed.  entryName is the text fields to
	    // display in the Alert window.

	    int pos = 0;
	    String entryName[] = {
		"Numerator 1", 		"Denominator 1", 
		"Numerator 2", 		"Denominator 2"
	    };

	    // Parse the data within a try statement in case there
	    // is bogus inputs.  If something fails, the catch will
	    // display an error message.
	    //

	    try {
		n1 = Long.parseLong(num1.getText());
		pos++;
		d1 = Long.parseLong(den1.getText());
		if (d1 == 0) {throw new Exception ("Zero Denominator");}
		pos++;
		n2 = Long.parseLong(num2.getText());
		pos++;
		d2 = Long.parseLong(den2.getText());
		if (d2 == 0) {throw new Exception ("Zero Denominator");}
	    }
	    
	    catch (Exception ex) {
		Alert error = new Alert(ex.getMessage(),
			entryName[pos] + ": Bad Input - Must be an integer",
			ftFrame.getLocationOnScreen());
		return;
	    }

	    // We got here, so the input values must be cool.
	    //
	    // Create two fractions, perform the operations,
	    // and update the results Labels
	    
	    fraction1 = new Fraction (n1, d1);
	    fraction2 = new Fraction (n2, d2);

            // add
	    ans = fraction1.add(fraction2);
	    add.setText(ans.toString());

	    ans = fraction1.add(n2);
	    add_int.setText(ans.toString());

            // subtract
	    ans = fraction1.subtract(fraction2);
	    subtract.setText(ans.toString());

	    ans = fraction1.subtract(n2);
	    subtract_int.setText(ans.toString());

            // multiply
	    ans = fraction1.multiply(fraction2);
	    multiply.setText(ans.toString());

	    ans = fraction1.multiply(n2);
	    multiply_int.setText(ans.toString());

            // divide
	    ans = fraction1.divide(fraction2);
	    divide.setText(ans.toString());

	    ans = fraction1.divide(n2);
	    divide_int.setText(ans.toString());

            // equals
	    if (fraction1.equals(fraction2)) {
		equals.setText("Equal");
	    } else {
		equals.setText("NOT-Equal");
	    }

	    // Testing the 
	    // Fraction.equals(Object other); method.
	    // is a bit tricky, since it has two states that return
	    // not equal:
	    //
	    // 1) The item is not a Fraction (or derived from the Fraction
	    //     class
	    // 2) the item is a Fraction but is not equal to
	    //    the original Fraction.
	    //
	    // This code tests the first condition by  comparing the
	    // upper equals_int test with a label - which will hit
	    // the "Object equals" method, and fail for the object
	    // not being a Fraction.
	    //

	    if (fraction1.equals(new Long(n2))) {
		equals_int.setText("Equal");
	    } else {
		equals_int.setText("NOT-Equal");
	    }
	    
	    // This section of code builds a new 'internal'
	    // class that can be used to exercise the
	    // Fraction.equals(Object other); method.
	    //
	    // The new class is an extension of the Fraction
	    // class, and thus responds true to the "is-a Fraction"
	    // test in Fraction.equals(Object other).
	    //
	    // The new object is defined as a member of the
	    // Object class, in order to force java to invoke the
	    // Fraction.equals(Object other) method.
	    //
	    // Since the new class is constructed with the same
	    // numerator and denominator as fraction1, it is expected
	    // that the result will always be equal for equalsObj
	    //
	    // The comparison is between an fraction2 and the new f3 object
	    // in the second case - (equalsObj_int), which allows
	    // us to test whether the Fraction class can return a
	    // false when an object is-a Fraction, but is not equal
	    // to the base object.

	    {
		class NewFraction extends Fraction {
		    protected NewFraction(long n, long d) {
			super(n, d);
		    }
		}

		Object f3 = new NewFraction(n1, d1);

		if (fraction1.equals(f3)) {
		    equalsObj.setText("Equal");
		} else {
		    equalsObj.setText("NOT-Equal");
		}

		if (fraction2.equals(f3)) {
		    equalsObj_int.setText("Equal");
		} else {
		    equalsObj_int.setText("NOT-Equal");
		}
	    }
    }

    /**
     * ActionPerformed watches for events (button clicks), and
     * performs the appropriate action.
     * 
     * @param e      The ActionEvent that triggered entering this method.
     */

    public void actionPerformed(ActionEvent e) {
        String eventString = e.getActionCommand();

	// Clear the results labels

	add.setText(" ");
	add_int.setText(" ");
	subtract.setText(" ");
	subtract_int.setText(" ");
	multiply.setText(" ");
	multiply_int.setText(" ");
	divide.setText(" ");
	divide_int.setText(" ");
	equals.setText(" ");
	equals_int.setText(" ");
	equalsObj.setText(" ");
	equalsObj_int.setText(" ");

	// Get the first char from the eventString, and use that
	// to distinguish which button was clicked.
	// Q - Quit
	// R - Reset the input fields
	// H - Get Help
	// G - Go (run the test.)

	switch (eventString.getBytes()[0]) {
	case 'Q': {
	    System.exit(-1);
	    break;
	} // case 'Q'

	case 'R': {
		num1.setText("");
		num2.setText("");
		den1.setText("");
		den2.setText("");
	    break;
	} // case 'R'

	case 'H': {
	    String[] helpTxt = {
		"Enter the numerator and denominator",
		"for fraction 1 and fraction 2",
		"into the boxes below the buttons",
		"",
		"Click the 'Go' button to perform the tests",
		"",
		"Click the 'Quit' button to exit",
		"",
		"Copyright 1999, Fictional Corp",
		"Revision 1.0"
	    };
	    Alert help = new Alert("Help",
				helpTxt,
				ftFrame.getLocationOnScreen());
	    break;
	} // case 'H'

	case 'G': {
	    doTestsAndUpdates();
	    break;
	} // case 'G'
	} // switch
    }

    /**
     * React to a 'close' window event by closing the window, and
     * exiting the process.
     */

    public void windowClosing(WindowEvent event) {
        ftFrame.dispose();
        System.exit(0);
    } // windowClosing()
  

    // Remember the main
    /**
     * The main method just creates an instantiation of the
     * FractionTest object.
     */

    public static void main(String argv[]) {
	FractionTest n = new FractionTest();
    }

}




















