//=====================================================================
// File:    OptionDialog.java
// Class:   OptionDialog
// Package: AFLPgui
//
// Author:  James J. Benham (plus a little defiling by Philip DeCamp)
// Date:    August 12, 1998
// Contact: james_benham@hmc.edu or decamp@portalofevil.com
//
// Genographer v1.6 - Computer assisted scoring of gels.
// Changes Copyright (C) 2001 James J. Benham
// Copyright (C) 1998  Montana State University
// 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; version 2
// of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// The GNU General Public License is distributed in the file GPL
//=====================================================================

package AFLPgui;

import java.awt.Button;
import java.awt.Choice;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.Frame;
import java.awt.Label;
import java.awt.Panel;
import java.awt.ScrollPane;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import AFLPcore.Option;
import AFLPcore.MissingParameterError;

/**
 * This class will display options in a dialog box. The options are
 * associated with a GUI component by keeping the components in a parrallel
 * array. The components are displayed on the left side of the dialog, and
 * if the number of options cause the display space required to exceed the
 * size of the dialog, a scroll bar becomes available. The dialog will
 * check to see if all of the required options have been set before 
 * closing.
 *
 * @see AFLPcore.Option
 *
 * @author James J. Benham
 * @version 1.0.0
 * @date August 12, 1998
 */

public class OptionDialog extends Dialog implements ActionListener,
                                                    WindowListener
{
  // GUI Layout constants
  private static final int OPTION_WIDTH   = 300;
  private static final int OPTION_HEIGHT  =  25;
  private static final int LABEL_WIDTH    = 100;
  private static final int FIELD_WIDTH    = 150;
  private static final int HEADER_INSET   =   5;
  private static final int HEADER_HEIGHT  =  22;
  private static final int OPTION_INSET   =  12;
  private static final int OPTION_SPACE   =   5;
  private static final int BUTTON_WIDTH   =  70;
  private static final int BUTTON_HEIGHT  =  22;
  private static final int BUTTON_V_SPACE =   5;
  private static final int BUTTON_H_INSET =   5;
  private static final int V_INSET        =  25;
  private static final int SCROLL_V_INSET =  20;
  private static final int SCROLL_WIDTH   =  16;
  private static final int WIDTH = (OPTION_WIDTH + BUTTON_WIDTH + 
                        2*BUTTON_H_INSET);
  private static final int HEIGHT = 320;

  private Panel optionPane;
  private Button okButton;
  private Button cancelButton;
  private Component optUIComponent[];

  private Option opts[];
  private int numRequired;
  private boolean canceled;

  private Frame parentWin;

  /**
   * Creates a new option dialog with the specified parameters.
   *
   * @param opts  the options to display
   * @param parent the owner of this dialog box.
   */
  public OptionDialog(Option opts[], Frame parent)
  {
    this(opts, parent, "");
  }

  /**
   * Creates a new option dialog with the specified parameters
   *
   * @param opts  the options to display
   * @param parent the owner of this dialog box.
   * @param title  the title of the dialog box.
   */
  public OptionDialog(Option opts[], Frame parent, String title)
  {
    // create a modal dialog box
    super(parent, title, true);

    parentWin = parent;

    this.opts = opts;
    optUIComponent = new Component[opts.length];
    canceled = true;

    // See how many are required
    numRequired = 0;
    for(int i=0; i < opts.length; i++)
      {
     if(opts[i].isRequired())
       numRequired++;
      }

    layoutOptions();

    addWindowListener(this);
  }

  /**
   * Reads the options in from the various components.
   *
   * @return <code>true</code> if all of the required options have been
   *         set, <code>false</code> otherwise.
   *
   * @exception NumberFormatException occurs if one of the options which
   *            is suppossed to be a number is cannot be read as a number.
   */
  private boolean readOptions()
  {
    boolean requiredSet;
    String value;
    TextField field;
    Choice choice;
	
    for(int i=0; i < opts.length; i++) {
	switch(opts[i].getType())
	    {
	    case Option.NUMBER:
		field = (TextField) optUIComponent[i];
		value = field.getText();
		if(!value.equals("")) {
		    try {
			Double db = new Double(value);
			opts[i].setValue(db.doubleValue());
		    }
		    catch(NumberFormatException e) {
			throw new NumberFormatException(opts[i].getLabel() + 
							" must be a number!");
		    }
		}
		break;
	    case Option.STRING:
		field = (TextField) optUIComponent[i];
		value = field.getText();
		if(!value.equals(""))
		    opts[i].setValue(value);
		break;
	    case Option.CHOICE:
		choice = (Choice) optUIComponent[i];
		// just get the selected one.
		opts[i].setValue(choice.getSelectedItem());
		break;
	    }
    }

    // see if all of the required parameters have been set
    requiredSet = true;
    for(int i=0; i < opts.length; i++)
	requiredSet = requiredSet && (!opts[i].isRequired() || 
				      opts[i].isSet());

    return requiredSet;
  }

  /**
   * Gives the options as they were set by the user in the dialog box.
   *
   * @return the options for the dialog box.
   */
  public Option[] getOptions()
  {
    return opts;
  }

  /**
   * Tells whether the dialog was canceled or not.
   *
   * @return <code>true</code> if the dialog was cancelled.
   */
  public boolean isCanceled()
  {
    return canceled;
  }

  /**
   * Handles the buttons. If ok is clicked, then the options are read.
   * if all of the options are set, then it closes the dialog. If not,
   * then an error is shown. Cancel simply closes the dialog.
   */
  public void actionPerformed(ActionEvent e)
  {
    try{
      if(e.getSource() == okButton)
	{
	  boolean allSet = readOptions();
	  if(allSet)
	    {
	      canceled = false;
	      dispose();
	    }
	  else
	    {
	      ErrorDialog eD = new ErrorDialog(parentWin);
	      eD.showError(new MissingParameterError("All Required optionst " +
						     " not set."));
	    }
	}
      if(e.getSource() == cancelButton)
	{
	  canceled = true;
	  dispose();
	}
    } catch(Throwable error) {
      ErrorDialog eD = new ErrorDialog(parentWin);
      eD.showError(error);
    }
  }

  /**
   * Closes the dialog and is equivalent to pressing cancel.
   */
  public void windowClosing(WindowEvent e)
  {
    canceled = true;
    dispose();
  }

  /**
   * Adds all of the components to the dialog box.
   */
  private void layoutOptions()
  {
    int x;
    int y;

    setLayout(null);
    setSize(WIDTH, HEIGHT);
    setResizable(false);

    // find out how long this will be
    int length = ((V_INSET - SCROLL_V_INSET) + 
            opts.length * (OPTION_HEIGHT + OPTION_SPACE));

    // add room for the required label
    if(numRequired > 0)
      length += OPTION_SPACE + HEADER_HEIGHT;

    // add room for the optional label
    if(opts.length > numRequired)
      length += OPTION_SPACE + HEADER_HEIGHT;

    // make the scroll pane
    ScrollPane scrollArea = new ScrollPane();
    add(scrollArea);
    scrollArea.setBounds(0, SCROLL_V_INSET, OPTION_WIDTH, 
                HEIGHT - SCROLL_V_INSET);

    // create a panel for the options
    optionPane = new Panel();
    // subtract the width of a scroll bar, can be found by
    //  scrollArea.getVScrollbarWidth(), but we must wait around
    // for the peer to be created, which it wasn't been at this point.
    // It simply returns 0. So, set it as a constant and just go
    // with it. It sucks b/c it's system dependent, but it works.
    optionPane.setBounds(0, 0, 
                OPTION_WIDTH - 4 - SCROLL_WIDTH, length);
    optionPane.setLayout(null);

    // Add the options panel to a scroll pane
    scrollArea.add(optionPane);

    // ===========add the buttons==========
    okButton = new Button("Ok");
    add(okButton);
    okButton.addActionListener(this);
    x = OPTION_WIDTH + BUTTON_H_INSET;
    y = V_INSET;
    okButton.setBounds(x, y, BUTTON_WIDTH, BUTTON_HEIGHT);
    y += BUTTON_HEIGHT + BUTTON_V_SPACE;

    cancelButton = new Button("Cancel");
    add(cancelButton);
    cancelButton.addActionListener(this);
    cancelButton.setBounds(x, y, BUTTON_WIDTH, BUTTON_HEIGHT);

    //===========draw required label=============
    x = 0;
    y = V_INSET - SCROLL_V_INSET;
    // Optional vs Required Distiction in display removed by Philip DeCamp
    // Re-inserted by jbenham Sept 3, 2001
    if(numRequired > 0 && numRequired != opts.length)	{
	Label requiredL = new Label("Required Parameters____________________");
	optionPane.add(requiredL);
	requiredL.setBounds(HEADER_INSET, y, OPTION_WIDTH - HEADER_INSET,
			    HEADER_HEIGHT);
	y += OPTION_SPACE + HEADER_HEIGHT;
    }
	
	//============ add the required options =============
    for(int i=0; i < opts.length; i++)	{
	if(opts[i].isRequired()) {
	    addOption(opts[i], y, i);
	    y += OPTION_HEIGHT + OPTION_SPACE;
	}
    }

    //============add the optional label===========
    if(opts.length > numRequired) {
	Label optionalL;
	optionalL = new Label("Optional Parameters____________________");
	optionPane.add(optionalL);
	optionalL.setBounds(HEADER_INSET, y, OPTION_WIDTH - HEADER_INSET,
			    HEADER_HEIGHT);
	y += OPTION_SPACE + HEADER_HEIGHT;
    }

    //============ add the optional options =============
    for(int i=0; i < opts.length; i++) {
	if(!opts[i].isRequired()) {
	    addOption(opts[i], y, i);
	    y += OPTION_HEIGHT + OPTION_SPACE;
	}
    }

    // Remove option display code that ignores required vs optional
    // jbenham Sept 3, 2001
    /*
     *for(int i = 0; i < opts.length; i++){
     *	addOption(opts[i], y, i);
     *  y+= OPTION_HEIGHT + OPTION_SPACE;
     *}
     */
  }

  /**
   * Creates a GUI component for the specified option and places it
   * at the specified position. The component is then stored in an
   * array at the specified index.
   *
   * @param option   the option to add to the display
   * @param pos      the vertical position to add it at
   * @param index    the index into the array of GUI components. This
   *                 is the same as the index of the option in the
   *                 option array.
   */
  public void addOption(Option option, int pos, int index)
  {
    // Add the label
    Label label = new Label(option.getLabel() + ":");
    optionPane.add(label);
    if(option.getType() == Option.LABEL)
	label.setBounds(OPTION_INSET, pos, WIDTH, OPTION_HEIGHT);
    else
	label.setBounds(OPTION_INSET, pos, LABEL_WIDTH, OPTION_HEIGHT);

    // Add the component
    switch(option.getType())
      {
      case Option.STRING: // just a text box like the number, 
                       // difference in reading
      case Option.NUMBER:
     
	  TextField tempField = new TextField();
	  
	  if(option.getType() == Option.NUMBER){
	      Double tempInt = new Double(option.getNumValue());
	      tempField.setText(tempInt.toString());
	  }
	  else
	      tempField.setText(option.getStringValue());
	  
	  optionPane.add(tempField);
	  tempField.setBounds(OPTION_INSET + LABEL_WIDTH, pos,
			      FIELD_WIDTH, OPTION_HEIGHT);
	  optUIComponent[index] = tempField;
	  break;

      case Option.CHOICE:
	  Choice tempChoice = new Choice();
	  
	  // add the choices
	  String choices[] = option.getChoices();
	  for(int i=0; i < choices.length; i++)
	      tempChoice.add(choices[i]);
	  
	  if(option.getDefaultChoice() != null)
	      tempChoice.select(option.getDefaultChoice());
	  
	  optionPane.add(tempChoice);
	  tempChoice.setBounds(OPTION_INSET + LABEL_WIDTH, pos,
			       FIELD_WIDTH, OPTION_HEIGHT);
	  optUIComponent[index] = tempChoice;
	  break;
      }
  }

  // ==================Unused methods required by interfaces=================
  /**Unused*/ public void windowOpened(WindowEvent e) {}
  /**Unused*/ public void windowClosed(WindowEvent e) {}
  /**Unused*/ public void windowIconified(WindowEvent e) {}
  /**Unused*/ public void windowDeiconified(WindowEvent e) {}
  /**Unused*/ public void windowActivated(WindowEvent e) {}
  /**Unused*/ public void windowDeactivated(WindowEvent e) {}
}
