import sys
import os

# Append the parent directory where function.py is located.
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../..")))

# Import the function module.
import function

import random
import os

# ANSI escape sequences for colors and text styles.
RESET     = "\033[0m"
BOLD      = "\033[1m"
UNDERLINE = "\033[4m"
RED       = "\033[31m"
GREEN     = "\033[32m"
YELLOW    = "\033[33m"
BLUE      = "\033[34m"
MAGENTA   = "\033[35m"
CYAN      = "\033[36m"
WHITE     = "\033[37m"

def clear_screen():
    """
    Clear the terminal screen using the appropriate system command.
    """
    os.system('cls' if os.name == 'nt' else 'clear')

def print_title():
    """
    Print the title header for the Alternate Hangman game.
    
    This header uses a different style with a bordered design.
    """
    clear_screen()
    title = f"{BOLD}{CYAN}+{'-'*34}+{RESET}\n"
    title += f"{BOLD}{CYAN}|{' ' * 34}|{RESET}\n"
    title += f"{BOLD}{CYAN}|{' ' * 10}H A N G M A N{' ' * 10}|{RESET}\n"
    title += f"{BOLD}{CYAN}|{' ' * 34}|{RESET}\n"
    title += f"{BOLD}{CYAN}+{'-'*34}+{RESET}\n"
    print(title)

# Alternate Hangman drawing states (using classic gallows).
HANGMAN_STATES = [
    # State 0: No wrong guesses.
    f"""
   {YELLOW}_______{RESET}
  {YELLOW}|/     {RESET}
  {YELLOW}|      
  {YELLOW}|      
  {YELLOW}|      
  {YELLOW}|      
{YELLOW}__|__{RESET}
""",
    # State 1: Head.
    f"""
   {YELLOW}_______{RESET}
  {YELLOW}|/     |{RESET}
  {YELLOW}|      {RESET}{RED}(_){RESET}
  {YELLOW}|      
  {YELLOW}|      
  {YELLOW}|      
{YELLOW}__|__{RESET}
""",
    # State 2: Head and body.
    f"""
   {YELLOW}_______{RESET}
  {YELLOW}|/     |{RESET}
  {YELLOW}|      {RESET}{RED}(_){RESET}
  {YELLOW}|       {RED}|{RESET}
  {YELLOW}|       
  {YELLOW}|      
{YELLOW}__|__{RESET}
""",
    # State 3: Head, body, and one arm.
    f"""
   {YELLOW}_______{RESET}
  {YELLOW}|/     |{RESET}
  {YELLOW}|      {RESET}{RED}(_){RESET}
  {YELLOW}|      {RED}\\|{RESET}
  {YELLOW}|       
  {YELLOW}|      
{YELLOW}__|__{RESET}
""",
    # State 4: Head, body, and two arms.
    f"""
   {YELLOW}_______{RESET}
  {YELLOW}|/     |{RESET}
  {YELLOW}|      {RESET}{RED}(_){RESET}
  {YELLOW}|      {RED}\\|/{RESET}
  {YELLOW}|       
  {YELLOW}|      
{YELLOW}__|__{RESET}
""",
    # State 5: Head, body, two arms, and one leg.
    f"""
   {YELLOW}_______{RESET}
  {YELLOW}|/     |{RESET}
  {YELLOW}|      {RESET}{RED}(_){RESET}
  {YELLOW}|      {RED}\\|/{RESET}
  {YELLOW}|       {RED}|{RESET}
  {YELLOW}|      {RED}/ {RESET}
{YELLOW}__|__{RESET}
""",
    # State 6: Complete figure.
    f"""
   {YELLOW}_______{RESET}
  {YELLOW}|/     |{RESET}
  {YELLOW}|      {RESET}{RED}(_){RESET}
  {YELLOW}|      {RED}\\|/{RESET}
  {YELLOW}|       {RED}|{RESET}
  {YELLOW}|      {RED}/ \\{RESET}
{YELLOW}__|__{RESET}
"""
]

def draw_figure(num_wrong, max_wrong):
    """
    Draw the current Hangman figure based on the number of wrong guesses.
    
    :param num_wrong: The number of incorrect guesses.
    :param max_wrong: The maximum allowed wrong guesses.
    """
    index = min(num_wrong, len(HANGMAN_STATES) - 1)
    print(HANGMAN_STATES[index])

def display_status(current_pattern, guessed_letters, num_wrong, max_wrong):
    """
    Display the current game status including the hangman drawing,
    a bordered display of the current word pattern, guessed letters,
    and the number of remaining attempts.
    
    :param current_pattern: The current pattern of the word (with dashes for unknown letters).
    :param guessed_letters: A set of letters already guessed.
    :param num_wrong: The number of wrong guesses made.
    :param max_wrong: The maximum allowed wrong guesses.
    """
    print_title()
    # Draw the hangman figure.
    draw_figure(num_wrong, max_wrong)
    
    # Create a decorative box for the current word pattern.
    pattern_box_width = len(current_pattern) * 4 + 1
    print(BLUE + "+" + "-" * pattern_box_width + "+" + RESET)
    pattern_line = "|"
    for char in current_pattern:
        pattern_line += f" {BOLD}{char}{RESET} |"
    print(BLUE + pattern_line + RESET)
    print(BLUE + "+" + "-" * pattern_box_width + "+" + RESET)
    print()
    
    # Display guessed letters.
    if guessed_letters:
        print(BOLD + "Guessed Letters:" + RESET, ", ".join(sorted(guessed_letters)))
    else:
        print(BOLD + "Guessed Letters:" + RESET, "None")
    
    # Display remaining attempts.
    print(BOLD + "Remaining Attempts:" + RESET, f"{max_wrong - num_wrong}")
    print()

def get_user_guess(guessed_letters):
    """
    Prompt the user for a letter guess and validate the input.
    
    :param guessed_letters: The set of letters already guessed.
    :return: A lowercase letter that has not been guessed.
    """
    while True:
        guess = input("Enter your guess (a single letter): ").strip().lower()
        if len(guess) != 1 or not guess.isalpha():
            print(RED + "Please enter a single alphabet letter." + RESET)
        elif guess in guessed_letters:
            print(RED + "You already guessed that letter. Try again." + RESET)
        else:
            return guess

def get_word_length(word_dict: dict) -> int:
    """
    Prompt the user to choose a desired word length and return the available words of that length.
    
    :param word_dict: The dictionary containing words categorized by length.
    :return: A tuple (length, word_list) where word_list contains words of the chosen length.
    """
    while True:
        try:
            length = input("Enter desired word length: ").strip()
            if length not in word_dict:
                print(RED + f"No words of length {length} available. Please try a different length." + RESET)
            else:
                return int(length), word_dict[length]
        except ValueError:
            print(RED + "Invalid input. Please enter a valid number." + RESET)

def get_max_attempts():
    """
    Prompt the user to enter the maximum number of wrong guesses allowed.
    
    :return: The maximum number of wrong guesses as an integer.
    """
    while True:
        try:
            max_attempts = int(input("Enter maximum number of wrong guesses allowed (e.g., 6): ").strip())
            if max_attempts < 1:
                print(RED + "Please enter a positive number." + RESET)
            else:
                return max_attempts
        except ValueError:
            print(RED + "Invalid input. Please enter a number." + RESET)

def generate_pattern(word, guess, current_pattern):
    """
    Generate a new pattern string for the given word based on the guessed letter.
    
    Each letter in the word that matches the guess is revealed; otherwise, the current pattern character is retained.
    
    :param word: The word being considered.
    :param guess: The current guessed letter.
    :param current_pattern: The current known pattern of the word.
    :return: A new pattern string with the guessed letter revealed.
    """
    new_pattern = ""
    for i, letter in enumerate(word):
        if letter == guess:
            new_pattern += guess
        else:
            new_pattern += current_pattern[i]
    return new_pattern

def choose_family(possible_words, guess, current_pattern):
    """
    Partition the possible words into families based on the new pattern produced by the guessed letter.
    
    Selects the family (pattern and set of words) with the largest number of words. If there's a tie, it prefers
    the family that reveals the fewest occurrences of the guessed letter.
    
    :param possible_words: A set of possible words.
    :param guess: The guessed letter.
    :param current_pattern: The current pattern of the secret word.
    :return: A tuple (new_pattern, new_possible_words) for the chosen family.
    """
    families = {}
    for word in possible_words:
        pattern = generate_pattern(word, guess, current_pattern)
        families.setdefault(pattern, set()).add(word)
    
    best_pattern = None
    best_family = set()
    for pattern, words in families.items():
        if len(words) > len(best_family):
            best_family = words
            best_pattern = pattern
        elif len(words) == len(best_family):
            # Tie-breaker: choose the pattern that reveals fewer new letters.
            current_count = best_pattern.count(guess) if best_pattern else float('inf')
            new_count = pattern.count(guess)
            if new_count < current_count:
                best_family = words
                best_pattern = pattern
    return best_pattern, best_family

def play_hangman(word_dict: dict):
    """
    Play an advanced Evil Hangman game with the alternate visual interface.
    
    In this version, the computer dynamically adjusts the secret word family to keep the game challenging.
    The user selects the word length and the maximum number of wrong guesses, then attempts to guess the word.
    :param word_list: Word list. A game will be chosen from this word list.
    """
    print_title()
    print(BOLD + GREEN + "Welcome to Alternate Hangman!" + RESET)
    print()
    
    word_length, filtered_words = get_word_length(word_dict)
    max_attempts = get_max_attempts()
    
    possible_words = set(filtered_words)
    current_pattern = "-" * word_length
    guessed_letters = set()
    num_wrong = 0
    
    while True:
        display_status(current_pattern, guessed_letters, num_wrong, max_attempts)
        
        if "-" not in current_pattern:
            print(GREEN + BOLD + "Congratulations! You've guessed the word!" + RESET)
            break
        
        if num_wrong >= max_attempts:
            secret_word = random.choice(list(possible_words))
            print(RED + BOLD + f"Game Over! The word was: {secret_word}" + RESET)
            break
        
        guess = get_user_guess(guessed_letters)
        guessed_letters.add(guess)
        
        new_pattern, new_possible_words = choose_family(possible_words, guess, current_pattern)
        
        if new_pattern == current_pattern:
            num_wrong += 1
            print(RED + "Incorrect guess." + RESET)
        else:
            current_pattern = new_pattern
            print(GREEN + "Correct guess!" + RESET)
        
        possible_words = new_possible_words

def main(word_list):
    """
    Run the main game loop for the alternate Hangman game.
    
    After each game, the user is prompted to play again or exit.
    :param word_list: Word list. A game will be chosen from this word list.
    """
    while True:
        play_hangman(word_list)
        choice = input(MAGENTA + "Press Enter to play again or type 'exit' to quit: " + RESET).strip().lower()
        if choice == 'exit':
            print(BOLD + CYAN + "Thank you for playing Alternate Hangman! Goodbye." + RESET)
            break

def init(set_language: str = 'en'):
    """
    Initialize the Hangman module and start the game.
    
    This function serves as the entry point for the module. Call init() to launch the game.

    :param set_language: Set words language: en, tr, ru, sp, it, fr
    """
    filename = os.path.abspath(__file__)
    dir = os.path.dirname(filename)
    word_dict = function.File().json_read(f'{dir}/data/{set_language}.json') if set_language in ['en', 'tr', 'ru', 'sp', 'it', 'fr'] else function.File().json_read(f'{dir}/data/en.json')
    
    main(word_dict)