import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.text.*;
import java.util.*;

/** This class manages a window with up to 7 int fields (numbered 0, ..., 6), 
 *  7 double fields, and 7 string fields, a choice box for locales, and a 
 *  "ready" button. The user is expected to type values in fields and then 
 *  click the "ready" button. When this button is clicked, method buttonPressed 
 *  is called to process the values in the fields and deliver some output,
 *  possibly in the fields in the window.
 *
 *  Thus, the window is a general GUI, which can be used in a number of
 *  situations. 
 *
 *  To use this class, one needs to write a class X that 
 *
 *  (1) Creates an instance of this class (which we will call "view")
 * 
 *  (2) Registers an ActionListener with the button via the command
 *      view.getButton().addActionListener(this);
 *  
 *  (3) Implements actionPerformed(ActionEvent), which is the only
 *      method in ActionListener.  
 * 
 *  This method actionPerformed(ActionEvent) is called when the "ready"
 *  button is pressed. In this method, place any code you like to read the
 *  values of the fields, calculate, and store values in the fields. To 
 *  read the fields, use methods getIntField, getDoubleField, and 
 *  getStringField. To store values in the fields, use methods setIntField, 
 *  setDoubleField, and setStringField.
 */
public class GenericGUI extends JFrame {
    // constants
    // TOTAL_MAX_FIELDS: max number of fields of one type that is allowed
    private static final int TOTAL_MAX_FIELDS= 7;
    private static final int MAX_FIELD_LENGTH= 20;
    private static final int ROW_SEPARATION= 10;
    private static final int COLUMN_SEPARATION= 15;

    // maxFields: actual max number of fields used of one type.
    private static int maxFields;

    // fields
    private JTextField[] intFields;     // the fields that can contain ints
    private JTextField[] doubleFields;  // the fields that can contain doubles
    private JTextField[] stringFields;  // the fields that can contain text
    private int numInts;                // number of int fields
    private int numDoubles;             // number of double fields
    private int numStrings;             // number of text fields
    
    // the button
    private JButton theButton;

    // These variables are used to provide the choicebox of locales
    private Locale locale= Locale.getDefault();
    private Locale[]  locales= Calendar.getAvailableLocales();  // array of all locales
    private JComboBox choiceBox;

    /** Constructor: a GUI window with
     *  max( min(i,MAX_FIELDS), 0) int fields,
     *  max( min(d,MAX_FIELDS), 0) double fields,
     *  max( min(s,MAX_FIELDS), 0) String fields,
     * and a "ready" button */
    public GenericGUI(int i, int d, int s) {
        super("Generic GUI");
        numInts    = Math.max( Math.min(i,TOTAL_MAX_FIELDS), 0);
        numDoubles = Math.max( Math.min(d,TOTAL_MAX_FIELDS), 0);
        numStrings = Math.max( Math.min(s,TOTAL_MAX_FIELDS), 0);
        maxFields  = Math.max( Math.max(numInts,numDoubles), numStrings );
        
        intFields    = new JTextField[maxFields];
        doubleFields = new JTextField[maxFields];
        stringFields = new JTextField[maxFields];
        
        createFields();
        
        theButton    = createActionButton();

        // Tell the program to exit upon closure of this window
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        // Add the Panel of fields and the button to this window
        getContentPane().setLayout(new BorderLayout());
        choiceBox = createChoiceBox();
        getContentPane().add(choiceBox,BorderLayout.NORTH);
        getContentPane().add(createPanel(),BorderLayout.CENTER);
        getContentPane().add(theButton,BorderLayout.SOUTH);
    }
    
    /** Create elements of arrays intFields, doubleFields, and stringFields */
    private void createFields() {
        //inv: elts 0..i-1 have been added to each array
        for(int i= 0; i< maxFields; i++) {
            intFields[i]    = new JTextField(MAX_FIELD_LENGTH);
            doubleFields[i] = new JTextField(MAX_FIELD_LENGTH);
            stringFields[i] = new JTextField(MAX_FIELD_LENGTH);
        }
    }

    /** Yields: the panel of fields that goes in this window */
    private JPanel createPanel() {
        JPanel panel= new JPanel();
        panel.setLayout(new GridLayout(1, 3, COLUMN_SEPARATION, 0));
        if (numInts > 0) {
            panel.add(createFieldsPanel(numInts,    intFields,    "Integer fields"));
        }
        if (numDoubles > 0) {
            panel.add(createFieldsPanel(numDoubles, doubleFields, "Double fields"));
        }
        if (numStrings > 0) {
            panel.add(createFieldsPanel(numStrings, stringFields, "String fields"));
        }
        return panel;
    }

    /** Yields: a Panel with visible elements a[0..n-1], invisible elements 
     *  a[n..maxFields-1] (to make the vertical sizes nice), and with
     *  title t above them.
     *  Pre: n > 0 */
    private JPanel createFieldsPanel(int n, JTextField[] a, String t) {
        JPanel panel= new JPanel();
        panel.setLayout(new GridLayout(maxFields+1, 1, 0, ROW_SEPARATION));
        panel.add(new JLabel(t, JLabel.CENTER)); 
        //inv: elts 0..i-1 have been added
        for (int i= 0; i < n; i++) {
            panel.add(a[i]);
        }
        // Add the invisible elements.
        // inv: items n..i-1 invisible elts have been added
        for (int i=n; i < maxFields; i++) {
            panel.add(a[i]);
            a[i].setVisible(false);
        }
        return panel;
    }

    /** Yields: a button with title "Ready!" */
    private JButton createActionButton() {
        JButton button= new JButton("Ready!");
        return button;
    }

    /** Yields: the choiceBox to contain locales */
    private JComboBox createChoiceBox() {
        JComboBox cb = new JComboBox();

        // Add the locales to cb
        // inv: items 0..i-1 have been added
        for (int i= 0; i != locales.length; i++) {
            cb.addItem(locales[i].getDisplayName());
        }
        
        // Set the selected item to the default
        // inv: items 0..i-1 have been set to default
        for (int i= 0; i != locales.length; i++) {
            if (locale.equals(locales[i])) {
                cb.setSelectedIndex(i);
            }
        }
        
        return cb;
    }

    /** Pack and show this window */
    public void showWindow() {
        pack();
        setVisible(true);
    }
    
    // public methods to access the gui components

    /** Yields: the integer in int field number f, or 0 if either f is not
     *  valid or the field doesn't contain an integer. */
     public int getIntField(int f) {
         try {
             return Integer.parseInt( intFields[f].getText() );
         } catch (ArrayIndexOutOfBoundsException e) {
             return 0;
         } catch (NumberFormatException e) {
             intFields[f].setText("" + intFields[f].getText() +": NOT AN INTEGER, 0 used");
             return 0;
         }
     }

     /** Set int field number f to m.  No effect if f is not valid. */
     public void setIntField(int f, int m) {
         try {
             intFields[f].setText("" + m);
         } catch (ArrayIndexOutOfBoundsException e) {
             return;
         }
     }
     
     /** Yields: the double in double field number f, or 0 if either f is not
      *  valid or the field doesn't contain a double. */
     public double getDoubleField(int f) {
         try {
             return Double.valueOf( doubleFields[f].getText() ).doubleValue();
         } catch (ArrayIndexOutOfBoundsException e) {        
             return 0;
         } catch (NumberFormatException e) {     
             doubleFields[f].setText("" + doubleFields[f].getText() +": NOT A DOUBLE, 0 used");
             return 0;
         }
     }

     /** Set double field number f to d.  No effect if f is not valid. */
     public void setDoubleField(int f, double d) {
         try {
             doubleFields[f].setText("" + d);
         } catch (ArrayIndexOutOfBoundsException e) {
             return;
         }
     }

     /** Yields: the string in string field number f, or "" if f is not valid. */
     public String getStringField(int f) {
         try {
             return stringFields[f].getText();
         } catch (ArrayIndexOutOfBoundsException e) {
             return "";
         }
     }

     /** Set string field number f to s.  No effect if f is not valid. */
     public void setStringField(int f, String s) {
         try {
             stringFields[f].setText("" + s);
         } catch (ArrayIndexOutOfBoundsException e) {
             return;
         }
     }

     /** Yields: the "ready" button.  Needed to hook up ActionListener */
     public JButton getButton() {
         return theButton;
     }
     
     /** Yields:the locale that was selected from the choice box (the default, if none) */
     public Locale getLocale() {
         return locales[choiceBox.getSelectedIndex()];
     }

}