//=====================================================================
// File:    Option.java
// Class:   Option
// Package: AFLPcore
//
// Author:  James J. Benham
// Date:    August 10, 1998
// Contact: james_benham@hmc.edu
//
// Genographer v1.0 - Computer assisted scoring of gels.
// 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 AFLPcore;

/**
 * This class provides a way for other class to specify options and
 * parameters. There are three types of options supported by this class.
 * The first is a simply a string indicating that some sort of string
 * should be given as an option. The class that created this option should
 * check to see if the string matches whatever it needs. The second type is
 * a number. The third type is a choice, which consists of a list of
 * possible selections. Some other part of a program may wish to implement
 * a user interface for these different types of options. This way, a
 * program can query a class, get it's options, present the options to the
 * user, and return the users choices to the program. Parameters that the
 * class needs to operate should be marked as <code>required</code> in the
 * constructor. 
 *
 * @see Operation
 * @see AFLPgui.OptionsDialog
 *
 * @author James J. Benham
 * @version 1.0.0
 * @date August 10, 1998
 */

public class Option
{
  /** Constant for denoting option type*/
  public static final int STRING = 0;
  public static final int NUMBER = 1;
  public static final int CHOICE = 2;
  public static final int LABEL = 3;
  
  private int type;
  private String label;
  private boolean isRequired;
  private boolean isSet;
  private String[] choices;
  private String defaultChoice;
  
  private String stringValue;
  private double numValue;
  
  public Option(String label, int type, boolean required)
  {
	  
	this.label = label;
	if(type != LABEL)
	  throw new IllegalArgumentException("Invalid type for option creation." + 
				" Must be LABEL!");
	this.type = type;
	isRequired = required;
	isSet = true;
    numValue = 0;
    stringValue = null;
    choices = null;
  }
  
  	
  /**
   * Create a new option of the specified type with the specified label.
   * This constructor should be used to create options of the 
   * <code>STRING</code> or <code>NUMBER</code> type. The other constructor 
   * should be used to create an option of type <code>CHOICE</code>, since 
   * it also takes a list of values to choose from.
   *
   * @param label  the name of this option
   * @param type   one of the type in the class description 
   * @param required  determines if this "option" is required for the
   *     operation or if it is truly optional.
   *
   * @exception IllegalArgumentException occurs when the type is not
   *    one known to this class, or if it can only be created with the 
   *    other constructor because some options require more details than
   *    others.
   */
  public Option(String label, int type, boolean required, int defaultValue)
  {
    this.label = label;

    // Check the type.
    if(type != NUMBER)
      throw new IllegalArgumentException("Invalid type for option creation." +
					 " Must be a NUMBER.");
    
	this.type = type;
    isRequired = required;
    isSet = true;
    numValue = defaultValue;
    stringValue = null;
    choices = null;
  }
  
  /**
   * Create a new option of the specified type with the specified label.
   * This constructor should be used to create options of the 
   * <code>STRING</code> or <code>NUMBER</code> type. The other constructor 
   * should be used to create an option of type <code>CHOICE</code>, since 
   * it also takes a list of values to choose from.
   *
   * @param label  the name of this option
   * @param type   one of the type in the class description 
   * @param required  determines if this "option" is required for the
   *     operation or if it is truly optional.
   *
   * @exception IllegalArgumentException occurs when the type is not
   *    one known to this class, or if it can only be created with the 
   *    other constructor because some options require more details than
   *    others.
   */
  public Option(String label, int type, boolean required, String defaultString)
  {
    this.label = label;

    // Check the type.
    if(type != STRING)
      throw new IllegalArgumentException("Invalid type for option creation." +
					 " Must be STRING.");
    this.type = type;
    isRequired = required;

	if(defaultString.length() == 0)
		isSet = false;
	else
		isSet = true;

    numValue = 0;
    stringValue = defaultString;

    choices = null;
  }

  /**
   * Create a new option of the specified type with the specified label.
   * This constructor should be used to create options of the 
   * <code>CHOICE</code>. The other constructor should be used to create
   * options of other types. This is done because the choice requires
   * an extra bit of information: the possible choices.
   *
   * @param label  the name of this option
   * @param type   one of the type in the class description 
   * @param required  determines if this "option" is required for the
   *     operation or if it is truly optional.
   * @param values a list of strings representing the possible choices
   *     for this option.
   *
   * @exception IllegalArgumentException occurs when the type is not
   *    one known to this class that requires the extra <code>String[]</code>
   *    parameter. If the type is not one of these, the other constructor
   *    should be used and this one will create the error.
   */
  public Option(String label, int type, boolean required, String[] values)
  {
    this(label, type, required, values, null);
  }

  /**
   * Create a new option of the specified type with the specified label.
   * This constructor should be used to create options of the 
   * <code>CHOICE</code>. The other constructor should be used to create
   * options of other types. This is done because the choice requires
   * an extra bit of information: the possible choices.
   *
   * @param label  the name of this option
   * @param type   one of the type in the class description 
   * @param required  determines if this "option" is required for the
   *     operation or if it is truly optional.
   * @param values a list of strings representing the possible choices
   *     for this option.
   * @param default  the default choice to be taken for the possilbe
   *     list of values. If it is null, no default will be set.
   *
   * @exception IllegalArgumentException occurs when the type is not
   *    one known to this class that requires the extra <code>String[]</code>
   *    parameter. If the type is not one of these, the other constructor
   *    should be used and this one will create the error.
   */
  public Option(String label, int type, boolean required, String[] values,
		String defaultChoice)
  {
    this.label = label;
    
    // Check to see if the type is valid.
    if(type != CHOICE)
      throw new IllegalArgumentException("Invalid type for option creation." +
					 "Only CHOICE should be created with "+
					 "this constructor.");
    this.type = type;
    
    isRequired = required;

    isSet = false;

    choices = values;

    // Make sure the default is one of the values
    if(defaultChoice != null)
      {
	boolean inList = false;
	for(int i=0; i < choices.length; i++)
	  inList = inList || choices[i].equals(defaultChoice);

	if(!inList)
	  {
	    System.err.println("Default of " + defaultChoice + " not valid.");
	    defaultChoice = null;
	  }
      }

    this.defaultChoice = defaultChoice;

    numValue = 0;
    stringValue = null;
  }

  /**
   * Gives the string that represents the value of this option. If the option
   * type does not have a string value, or if the value has not been set,
   * the method will still work, but will simply return an error code.
   *
   * @return the string, or <code>null</code> if one of the error conditions
   *    above occurs.
   */
  public String getStringValue()
  {
    // See if the value has been set
    if(!isSet)
      return null;

    // Check the type
    if(type == NUMBER)
      return null;

    return stringValue;
  }

  /**
   * Gives the numerical value associated with this option.
   *
   * @return the value, -1 if the value has not been set or if the type
   *    of this option does not have a number value.
   */
  public double getNumValue()
  {
    // See if the value has been set
    if(!isSet)
      return -1;

    // Check the type
    if(type != NUMBER)
      return -1;

    return numValue;
  }

  /**
   * Sets the string value of this option to the specified value.
   *
   * @param setValue  the new value for this option.
   */
  public void setValue(String setValue)
  {
    stringValue = setValue;
    isSet = true;
  }

  /**
   * Sets the numerical value of this option to the specified value.
   *
   * @param setValue  the new value for this option.
   */
  public void setValue(double setValue)
  {
    numValue = setValue;
    isSet = true;
  }

  /**
   * Gives a label for this option that serves as an identifier for the
   * option.
   *
   * @return a descriptive name for this option.
   */
  public String getLabel()
  {
    return label;
  }

  /**
   * Gives the type of option for this class. Possible answers include
   * string, number, or choice. These can be identified by comparing the
   * returned value to the constants in this class.
   *
   * @return the numerical representation of the type of option
   */
  public int getType()
  {
    return type;
  }

  /**
   * Tells whether or not this option is required by an <code>Operation</code>
   *
   * @return <code>true</code> if it is required.
   */
  public boolean isRequired()
  {
    return isRequired;
  }

  /**
   * Tells whether or not this option has been set to a value. 
   *
   * @return <code>true</code> when it has been set.
   */
  public boolean isSet()
  {
    return isSet;
  }

  /**
   * Gives the list of possiblities for a choice option. For example,
   * consider a "color" option, which could have the choices of :
   * "red," "yellow," "green," and "blue".
   *
   * @return the choices, <code>null</code> if this option is not
   *    of type choice.
   */
  public String[] getChoices()
  {
    return choices;
  }

  /**
   * Gives the default choice for this option. If none is set or if
   * this is not a CHOICE option, it will be null.
   *
   * @return  the default value, or <code>null</code> if not set or not
   *   applicable.
   */
  public String getDefaultChoice()
  {
    return defaultChoice;
  }
}
