package hangman.game;

import hangman.util.DisplayWord;
import hangman.util.HangmanDictionary;

import java.util.*;


/**
 * This class represents a game of Hangman between:
 *  - a guesser that chooses letters automatically based on their commonality
 *  - a secret word keeper that automatically changes the secret word after each guess to maximize the remaining possible words
 *
 * @author Robert C. Duvall
 */
public class HangmanAutoGame_Original {
    // constants
    public static final String ALPHABET = "abcdefghijklmnopqrstuvwxyz";
    private static final String LETTERS_ORDERED_BY_FREQUENCY = "aeiou" + "etaoinshrldcumfpgwybvkxjqz";

    // word that is being guessed
    private String mySecretWord;
    // how many guesses are remaining
    private int myNumGuessesLeft;
    // what is shown to the user
    private DisplayWord myDisplayWord;
    // tracks letters guessed
    private StringBuilder myLettersLeftToGuess;
    // secret word keeper state
    private List<String> myRemainingWords;
    // guesser state
    private int myIndex;


    /**
     * Create Hangman game using words of the given length chosen from the given dictionary of words
     * and giving the user the given number of chances.
     */
    public HangmanAutoGame_Original(HangmanDictionary dictionary, int wordLength, int numGuesses) {
        mySecretWord = dictionary.getRandomWord(wordLength).toLowerCase();
        myNumGuessesLeft = numGuesses;
        myLettersLeftToGuess = new StringBuilder(ALPHABET);
        myDisplayWord = new DisplayWord(mySecretWord);
        myRemainingWords = dictionary.getWords(wordLength);
        myIndex = 0;
    }

    /**
     * Play one complete game.
     */
    public void play () {
        boolean gameOver = false;
        while (!gameOver) {
            // print game status
            System.out.println();
            System.out.println(myDisplayWord);
            System.out.println(String.format("# misses left = %d", myNumGuessesLeft));
            System.out.println(String.format("letters not yet guessed = %s", myLettersLeftToGuess));
            // NOT PUBLIC, but makes it easier to test
            System.out.println(String.format("*** %s", mySecretWord));

            // handle guess
            String guess = "" + LETTERS_ORDERED_BY_FREQUENCY.charAt(myIndex++);
            if (guess.length() == 1 && Character.isAlphabetic(guess.charAt(0))) {
                int index = myLettersLeftToGuess.indexOf(guess);
                // do not count repeated guess as a miss
                if (index >= 0) {
                    System.out.println(String.format("letter guessed = %s", guess));
                    // record guess
                    myLettersLeftToGuess.deleteCharAt(index);
                    // create template of guesses and find one with most matching remaining words
                    char letter = guess.charAt(0);
                    Map<DisplayWord, List<String>> templatedWords = new HashMap<>();
                    for (String w : myRemainingWords) {
                        DisplayWord template = new DisplayWord(myDisplayWord);
                        template.update(letter, w);
                        if (!templatedWords.containsKey(template)) {
                            templatedWords.put(template, new ArrayList<>());
                        }
                        templatedWords.get(template).add(w);
                    }
                    int max = 0;
                    DisplayWord maxKey = new DisplayWord("");
                    for (Map.Entry<DisplayWord, List<String>> entry : templatedWords.entrySet()) {
                        if (entry.getValue().size() > max) {
                            max = entry.getValue().size();
                            maxKey = entry.getKey();
                        }
                    }
                    // update secret word to match template of guesses
                    myRemainingWords = templatedWords.get(maxKey);
                    Collections.shuffle(myRemainingWords);
                    mySecretWord = myRemainingWords.getFirst();
                    myDisplayWord = maxKey;
                    // check for guess in secret word
                    if (mySecretWord.indexOf(letter) < 0) {
                        myNumGuessesLeft -= 1;
                    }
                }
                else {
                    System.out.println("Please enter a new letter ...");
                }
            }
            else {
                System.out.println("Please enter a single letter ...");
            }

            // check for end of game
            if (myNumGuessesLeft == 0) {
                System.out.println("YOU ARE HUNG!!!");
                gameOver = true;
            }
            else if (myDisplayWord.equals(mySecretWord)) {
                System.out.println("YOU WIN!!!");
                gameOver = true;
            }
        }
        System.out.println(String.format("The secret word was %s", mySecretWord));
    }
}
