/*
 *  HiLo Class
 *
 * <p>
 * The classHi-Lo games. The objective of the game is for the user to
 * guess the computer generated secret number in the least number of 
 * tries. The low and high bounds of a secret number and the number of
 * guesses can be set by the programmer. If the number of guesses allowed 
 * is not set by the programmer, then the default value is determined
 * based on the low and high bounds. The default low and high bounds, 
 * if not set by the programmer are 1 and 100. The default number of guesses 
 * for the default low and high bounds is 7.
 *
 * @author Dr. Caffeine
 *
 */

class HiLo 
{

//--------------------------------
//    Data Members
//--------------------------------
  
    /**
     * The constant to signal the guess is invalid
     */
    public static final int INVALID  = -1;
    
    /**
     * The constant to signal the guess is high
     */
    public static final int HI       = 1;
    
    /**
     * The constant to signal the guess is low
     */
    public static final int LO       = 2;
    
    /**
     * The constant to signal the guess is correct
     */
    public static final int BINGO    = 3;
    
    /**
     * The constant to signal the game is over
     */
    public static final int GAMEOVER = 4;

    
    /**
     * The default maximum number of guesses
     */
    private static final int DEFAULT_MAX_GUESSES = 7;
    
    /**
     * The default low bound for the secret number range
     */
    private static final int DEFAULT_LOW         = 1;
    
    /**
     * The default high bound for the secret number range
     */
    private static final int DEFAULT_HIGH        = 100;
    
    /**
     * The maximum number of guesses allowed per game
     */
    private int   numberOfGuessesAllowed;
    
    /**
     * The low bound for the secret number range
     */
    private int   guessLowBound;
    
    /**
     * The high bound for the secret number range
     */
    private int   guessHighBound; 
    
    /**
     * The number of guess made by the player
     */
    private int   guessCount;
    
    /**
     * The randomly generated secret number
     */
    private int   secretNumber;

//--------------------------------
//    Constructors
//--------------------------------
    
    /**
     * Default constructor. 
     * Uses the default values for low, high, and the number
     * of guesses.
     */
    public HiLo()
    {
        this(DEFAULT_LOW, DEFAULT_HIGH, DEFAULT_MAX_GUESSES);
    }
   
    /**
     * Constructs a HiLo object with the given low and high values.
     * The number of guesses is computed from the values of low
     * and high.
     *
     * @param low  the low bound for the secret number
     * @param high the high bound for the secret number
     *
     */
    public HiLo(int low, int high) 
    {
        this(low, high, maxGuess(low, high));
    }

    /**
     * Constructs a HiLo object with the given values for low,
     * high, and the number of guesses.
     *
     * @param low       the low bound for the secret number
     * @param high      the high bound for the secret number
     * @param numGuess  the number of guesses allowed
     *
     */
    public HiLo(int low, int high, int numGuess) 
    {
        if (low > high) {
           int temp = low;
           low = high;
           high = temp;
        }
        
        if (numGuess <= 0) {
           numGuess = maxGuess(low, high);
        }
        
        guessLowBound  = low;
        guessHighBound = high;
        numberOfGuessesAllowed = numGuess;
        
        newGame();
     /*   
        System.out.println("low bound:     " + guessLowBound);          //TEMP
        System.out.println("high bound:    " + guessHighBound);         //TEMP
        System.out.println("max guesses:   " + numberOfGuessesAllowed); //TEMP
        System.out.println("secretNumber:  " + secretNumber);           //TEMP
        System.out.println("    ");                                     //TEMP
     */
    }

//-------------------------------------------------
//      Public Methods:
// 
//          int       getHighBound     (        )
//          int       getLowBound      (        )
//          int       getMaxGuess      (        )
//          int       getSecretNumber  (        )
//
//          void      newGame          (        )
//          int       nextGuess        ( int    )
//
//------------------------------------------------

   /**
    * Returns the high bound of the secret number range.
    *
    * @return the high bound of the secret number range
    */
   public int getHighBound( )
   {
      return guessHighBound;
   }
   
   /**
    * Returns the low bound of the secret number range.
    *
    * @return the low bound of the secret number range
    */
   public int getLowBound( )
   {
      return guessLowBound;
   }

   
   /**
    * Returns the maximum number of guesses allowed.
    *
    * @return the maximum number of guesses allowed
    */
   public int getMaxGuess( )
   {
      return numberOfGuessesAllowed;
   }
   
   /**
    * Returns the the secret number.
    *
    * @return the secret number
    */
   public int getSecretNumber( )
   {
      return secretNumber;
   }
   
  
   /**
    * Sets this HiLo object for a new game.    
    *
    */
   public void newGame ()
   {
      guessCount = 0;
      secretNumber = getRandomNumber( );
   }

   /**
    * Determines whether or not the guess is the secret number    
    *
    * @param guess  the next guess to evaluate
    *
    * @return the result of the test: HI, LO, BINGO, or GAMEOVER
    *
    */
   public int nextGuess ( int userGuess )
   {
        int status;
        
        guessCount++;
        
        if (userGuess == secretNumber) {
          
           status = BINGO;
        }
        else if (guessCount == numberOfGuessesAllowed) {
          
           status = GAMEOVER;
        }
        else if (!isValid(userGuess)) {
          
           status = INVALID;
        }
        else if (userGuess < secretNumber) {
          
           status = LO;
        }
        else {
           status = HI;
        } 
           
        return status;

   }

//-------------------------------------------------
//      Private Methods:
// 
//                 int      getRandomNumber  (                )
//                 boolean  isValid          (   int          )          
//          static int      maxGuess         (   int, int     )
//          
//------------------------------------------------
      
   /**
    * Computes a new secret number between low and high
    *
    * @return a random number between the range of low and high
    */
   private int getRandomNumber()
   {
      double x;
      int    number;
      
      x = Math.random();
      
      number = (int) Math.floor(x * (guessHighBound - 
                                     guessLowBound + 1)) + 
                                      guessLowBound;
      
     // System.out.println("New secret number: " + number); //TEMP
      
      return number;
   }

   /**
    * Determines if the argument is in the valid range
    *
    * @param guess  the number to check
    *
    * @return true if the guess is within the range; false otherwise
    */
   private boolean isValid(int guess)
   {
      boolean status;
      
      status = guessLowBound <= guess && guess <= guessHighBound;
      
      return status;
   }
   
   /**
    * Computes the number of allowed guesses.    
    *
    * @param low  the low bound of the secret number
    * @param high the high bound of the secret number
    *
    * @return the maximum number of guesses allowed
    *
    */
   private static int maxGuess(int low, int high)
   {
      return (int) Math.ceil(Math.log(high - low + 1) / Math.log(2));
   }
   
  
}
