// ConsoleInput.java
// Copyright (c) 2000, 2005 Dorothy L. Nixon.  All rights reserved.

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

/**
 * Contains static utility methods
 * for simplifed text input and
 * semi-automated program testing.
 *
 * @author D. Nixon
 */
public class ConsoleInput  {

   private static BufferedReader br =
                     new BufferedReader(
                         new InputStreamReader(System.in), 1);
   private static boolean echoWanted;

   /**
    * Reads a line of text from command-line input.
    * Input is echoed or not depending on value
    * set by method <code>setEchoWanted</code>.
    * (By default, input is not echoed, to avoid
    * redundancy with normal echoing of command-line
    * input.)  Intended for use in all kinds of
    * programs requiring command-line input.
    *
    * @exception RuntimeException if an IOException
    *            is thrown when reading the line.
    */
   public static String readLine()
   {
       try  {
          String input = br.readLine();
          if ( echoWanted )
             System.out.println(input);
          return input;
       } catch (IOException ioe)  {
          throw new RuntimeException(ioe);
       }  // catch
   }  // method readLine()

   /**
    * Causes input to be echoed or not, depending on
    * value of parameter.  Should be true for redirected
    * command-line input from a file, false for normal
    * command line input, for which echoing would be redundant.
    *
    * @param wanted - If true, causes input
    *         to be echoed.  If false, causes
    *         input not to be echoed.
    */
   public static void setEchoWanted(boolean echoWanted)
   {
      ConsoleInput.echoWanted = echoWanted;
   }  // method setEchoWanted

   /**
    * Prompte user for input and returns
    * one-line user reply.
    *
    * @param prompt the prompt, to which
    *        this method will append '>'
    * @return the String typed by the user
    *        until pressing ENTER.
    */
   public static String ask(String prompt)
   {
      System.out.print(prompt + ">");
      return readLine();
   }  // method ask(String)

   /**
    * Pauses the command-line display, prompting the user
    * to press ENTER to continue.  Blank lines are printed
    * before the prompt and after the user presses ENTER.
    */
   public static void pause()
   {
      System.out.println();
      System.out.print("Press ENTER to continue....");
      readLine();
      System.out.println();
   }  // method pause()

   /**
    * Prompts the user and ensures that the returned String
    * is one of the specified options.  Nags the user until
    * a valid response is obtained. The initial prompt will
    * consist of the specified question, followed by a
    * parenthetical list of the options, followed by ">".
    *
    * @param question the main text of the prompt
    * @param options array of permitted replies
    *
    * @return the user's reply, once the user enters
    *         a reply which contains the same sequence
    *         of characters (ignoring case for letters)
    *         as one of the specified options.
    * @exception NullPointerException if no options are
    *         provided, or if no question is provided.
    */
   public static String askSelection(String question,
                                     String[] options)
   {
      if ( options == null || options.length == 0 )  {
         throw new NullPointerException(
                            "No options provided for \"" +
                            question + "\"");
      } else  {
         String optionString = options[0];
         for ( int i = 1; i < options.length; i++ )
             optionString += ", " + options[i];
         String initialPrompt = question + " (" +
                                optionString + ")";
         String nagPrompt = "Please enter one of: {" +
                                optionString + "}";

         String answer = ask(initialPrompt);
         while ( !isOneOf(answer, options) )
            answer = ask(nagPrompt);
         return answer.toUpperCase();
      } // else
   }  // askSelection

   /**
    * Tests whether the specified character is equal,
    * ignoring case, to one of the specified options.
    *
    * @param toBeChecked the character to be tested.
    * @param options a set of characters
    * @return true if <code>toBeChecked</code> is
    *         equal, ignoring case, to one of the
    *         <code>options</code>, false otherwise.
    */
   public static boolean isOneOf(char toBeChecked,
                                 char[] options)
   {
      boolean oneOf = false;
      for ( int i = 0; i < options.length && !oneOf; i++ )
         if ( Character.toUpperCase(toBeChecked)
                   == Character.toUpperCase(options[i]) )
            oneOf = true;
      return oneOf;
   }  // method isOneOf(char, char[])

   /**
    * Tests whether the specified string is one of the
    * specified options.  Checks whether the string
    * contains the same sequence of characters (ignoring
    * case) as one of the specified options.
    *
    * @param toBeChecked the String to be tested
    * @param options a set of Strings
    * @return true if <code>toBeChecked</code>
    *         contains the same sequence of
    *         characters, ignoring case, as one of the
    *         <code>options</code>, false otherwise.
    */
   public static boolean isOneOf(String toBeChecked,
                                 String[] options)
   {
      boolean oneOf = false;
      for ( int i = 0; i < options.length && !oneOf; i++ )
         if ( toBeChecked.equalsIgnoreCase(options[i]) )
            oneOf = true;
      return oneOf;
   }  // method isOneOf(String, String[])

   /**
    * Prompts the user for a yes/no answer to the
    * specified question.  Nags the user until a valid
    * valid response ("Y", "N", "y", or "n") is obtained.
    *
    * @param question the question for which a
    *        yes/no answer is desired.
    * @return true if the user answers "Y" or "y".
    *         false if the user answers "N" or "n".
    */
   public static boolean askYesNo(String question)
   {
      String[] options = {"Y", "N"};
      String answer = askSelection(question, options);
      return answer.equalsIgnoreCase("Y");
   }  // method askUserYesNo
}  // class ConsoleInput