/*
 SENG111 Assignment I - the PNZ Game
 By Geoff Knagge (9806135)
*/

import java.awt.*;
import BreezyGUI.*;


public class PNZApplet extends GBApplet
{
  // The globals variables are all declared here
  String winningString;  
  int numberGuesses; 
  final String gameOverMsg = "Game Over. Select \"Start New Game\" to restart.";

  // set up the input section
  Label guessLabel = addLabel("Enter your guess here: ",6,1,1,1);
  TextField guessField = addTextField("",6,2,3,1);
  Button evaluate = addButton("Evaluate Guess",7,2,1,1);
  Button giveUp   = addButton("Give Up!",7,3,1,1);
  Button newGame  = addButton("Start New Game",7,4,1,1);

  // set up the feedback section
  TextArea history = addTextArea("",1,1, 6, 5);


  char randomDigit()   
  // returns a random character in the range '0' to '9' inclusive
  // no inputs
  {
    return (char)('0' + Math.random() * 10);
  }


  String randomString()
  // Generates a string of 3 unique digits
  // Inputs : none
  // Output : a string of length 3, each character is a unique numeric digit
  {
    String target = "\0\0\0"; //dummy characters to avoid out of range errors
    char digit;
    for (int cnt=0; cnt <3; cnt++)     //loop until we have 3 digits
     do
      {
        digit = randomDigit(); 
        if (    (target.charAt(0) != digit) 
	     && (target.charAt(1) != digit) 
	     && (target.charAt(2) != digit))
          target = digit + target;     // If unique, add it!
        else 
          digit = '-';                 // Otherwise, clear it
      }
     while (digit == '-');             // loop until we find a unique digit
    return target.substring(0,3);      // return the string of unique digits
  }


  String evaluateGuess(String target, String guess)
  // Compares the user's guess to the winning combination, and generates
  // feedback according to the rules of the PNZ Game.
  // Inputs : The winning combination, the user's guess
  // Output : A string of varying length containing the characters 
  //          'P','N','Z'
  {
   int perfectMatches = 0;
   int numberMatches = 0;
   String feedback = "";
   for (int cnt=0; cnt<3; cnt++)    // check all 3 digits for matches...
    {
      if (target.charAt(cnt)==guess.charAt(cnt))
        perfectMatches++;        // digit is in the right spot
      if    ( (target.charAt(0)==guess.charAt(cnt)) 
 	   || (target.charAt(1)==guess.charAt(cnt)) 
 	   || (target.charAt(2)==guess.charAt(cnt)))
        numberMatches++;         // digit is anywhere in the winning string
    }
   for (int cnt=0; cnt < perfectMatches; cnt++)
     feedback += 'P';   // add one "P" for each perfect match
   for (int cnt = perfectMatches; cnt < numberMatches; cnt++)
     feedback += 'N';   // the rest of the matches must be in the wrong spot
   if (numberMatches==0)
     feedback = "Z";    
   return feedback;
  }


  boolean guessValid(String guess)
  // checks to see if the user's guess is of length 3 and contains only
  // numeric digits, each of which are unique.
  // Input  : the user's guess
  // Output : true if the above conditions are met, false if not
  {
   if (guess.length()!=3)
    return false;   // wrong number of digits 
   for (int count1=0; count1<3; count1++) // validifying all 3 digits...
    {
      if ( ! Character.isDigit(guess.charAt(count1)) )
        return false;    // it wasn't a numeric digit
      for (int count2=count1+1; count2<3; count2++)
        if ((guess.charAt(count1)==guess.charAt(count2)) )
          return false; // the digit is repeated elsewhere in the string
    }
   return true;
  }


  void displayResults(String guess, String result) 
  // Input  : The user's guess, the results to be displayed
  // Output : none returned to caller, Inputs displayed on GUI
  {
   history.append("   "+guess+"   "+result+'\n'); //write to the text box
  }


  void disableInputs()
  // disables the inputs on the GUI so that no more guesses can be made after
  // the game has finished.
  {
    guessField.setEnabled(false);   //disable the input things so no
    evaluate.setEnabled(false);     //more guesses can be made
    giveUp.setEnabled(false); 
    history.append(gameOverMsg);
    guessField.setText(gameOverMsg);
  }

  void focusInput()
  // Gives the focus to the input field and selects the text in the field
  // so that it's more user friendly :-)
  {
   guessField.requestFocus();  // game still going: make input box active
   guessField.selectAll();  // and select text so it is easily overwritten
  }

 void processValidGuess(String guess)
 // Checks the user's guess to see how closely it matches, and returns 
 // appropriate feedback, including if the game has been won.
 // Input : The user's guess (assumed to have been checked for validity
 //         elsewhere in the program), the number of guesses made in this
 //	    game
 {
    String results = evaluateGuess(winningString,guess); //get the PNZ output
    numberGuesses++;
    displayResults(guess,results);

    if ( results.compareTo("PPP")==0 )   // if the game has been won...
      {
       disableInputs();	
       String cntStr;       // generate a message depending guess count...
       if (numberGuesses==1)
         cntStr="first guess!!!";
       else
         cntStr="in "+String.valueOf(numberGuesses)+" Guesses";
       messageBox("That's the correct combination!\nYou found it "+cntStr);
      }
    else
     focusInput();
 }

 void processGuess(String guess)
 // Check if the user's guess conforms to the rules. If so, go and process
 // it, otherwise return an error message.
 // Inputs : the user's guess, the number of guesses made during this game
 {
  guess = guess.trim();   // remove whitespace
  if (guessValid(guess))
    processValidGuess(guess);
  else 
    {
     focusInput();
     messageBox("That's an invalid Guess!\n"
               +"You must only enter 3 unique digits!");
    }
 }

 void startNewGame()   
 // reinitialise all the variables and objects in preparation for a new game
 {
  winningString = randomString(); // get a new set of 3 digits
  giveUp.setEnabled(true);        // ensure inputs are enabled 
  evaluate.setEnabled(true);
  guessField.setEnabled(true);  
  guessField.setText("");        // clear the input field
  guessField.requestFocus();     // activate input field - user friendly :-)
  numberGuesses = 0;               
  history.setEditable(false);    // stop user messing with the feedback :-P

  // the following long line sets up the instructions in feedback box
  history.setText("A number consisting of three unique digits has been"
                      +" chosen. The aim is\nto guess these digits in the "
                      +"correct order, using the provided feedback\nas a "
                      +"guide.\n\nFor each guess, a P will be displayed for"
                      +" each correct digit in the\ncorrect position, "
                      +"followed by an N for each digit that is correct, "
                      +"but\nin an incorrect position. A Z indicates that "
                      +"all digits are incorrect\n\nGuess  Feedback\n" );
 }

  public void buttonClicked(Button buttonObj)
  // Event handler for the clicking of one of the buttons. Determines which
  // has been pressed and calls the corresponding methods.
  // Input : the button which was pressed.
  {
   if (buttonObj==newGame)
    {
     startNewGame();
     history.setCaretPosition(0);
    }

   if (buttonObj==giveUp)
    {      
     history.append("Given up?! The correct combination was "+winningString+'\n');
     disableInputs();
     messageBox("Given up after "+String.valueOf(numberGuesses)+
	        " guesses?!\nThe correct combination was "+winningString);
    }

  if (buttonObj==evaluate)
    processGuess(guessField.getText());
  }

  public void init()
  // the class constructor to do some special initialisation
  {
   startNewGame(); 
  }


}
