import tkinter as tk
from tkinter import (
    messagebox, Tk, Label, Spinbox, Button, Text, END,
    StringVar, OptionMenu, Entry  # Added Entry here
)
from tkinter.scrolledtext import ScrolledText
from tkinter import font  # Imported font module
import threading
import subprocess as sub
import os
import sys
import shutil
import queue
from datetime import datetime
import re
import ast

# For memory limit (Unix-based systems)
if os.name != 'nt':
    import resource

# Initialize a thread-safe queue for inter-thread communication
msg_queue = queue.Queue()

# Initialize the main window
mainwin = Tk()
mainwin.title('Codeforces Hack Assistant')

# Define a bold font
bold_font = font.Font(weight='bold')

# GUI Layout
# Language Selection for Source Solution
source_lang_var = StringVar(value="C++")  # Default to C++
source_lang_label = Label(mainwin, text="Language:")
source_lang_label.grid(row=0, column=0, padx=(20, 0), pady=(10, 0), sticky='w')
source_lang_menu = OptionMenu(mainwin, source_lang_var, "C++", "Python")
source_lang_menu.grid(row=0, column=1, padx=(0, 20), pady=(10, 0), sticky='w')

# Language Selection for Compare Solution
compare_lang_var = StringVar(value="C++")  # Default to C++
compare_lang_label = Label(mainwin, text="Language:")
compare_lang_label.grid(row=0, column=16, padx=(20, 0), pady=(10, 0), sticky='w')
compare_lang_menu = OptionMenu(mainwin, compare_lang_var, "C++", "Python")
compare_lang_menu.grid(row=0, column=17, padx=(0, 20), pady=(10, 0), sticky='w')

Label(mainwin, text="Your Solution (C++/Python)").grid(row=1, column=0, columnspan=15)
Label(mainwin, text="Solution to Hack (C++/Python)").grid(row=1, column=16, columnspan=15)

source = ScrolledText(mainwin, height=10, width=60)
source.grid(row=2, column=0, padx=20, pady=(0, 50), columnspan=15)

compare = ScrolledText(mainwin, height=10, width=60)
compare.grid(row=2, column=16, padx=20, pady=(0, 50), columnspan=15)

Label(mainwin, text="Test Case Generator (PYTHON)").grid(row=3, column=0, columnspan=15)
generator = ScrolledText(mainwin, height=10, width=60)
generator.grid(row=4, rowspan=3, column=0, padx=20, pady=(0, 50), columnspan=15)

# New GUI Elements for Time and Memory Limits
# Time Limit
time_limit_label = Label(mainwin, text="Time Limit (seconds):")
time_limit_label.grid(row=4, column=16, padx=(20, 0), pady=(10, 0), sticky='w')
time_limit_var = StringVar(value="1")  # Default to 1 second
time_limit_entry = Entry(mainwin, textvariable=time_limit_var, width=10)
time_limit_entry.grid(row=4, column=17, padx=(0, 20), pady=(10, 0), sticky='w')

# Memory Limit
memory_limit_label = Label(mainwin, text="Memory Limit (MB):")
memory_limit_label.grid(row=5, column=16, padx=(20, 0), pady=(10, 0), sticky='w')
memory_limit_var = StringVar(value="256")  # Default to 256 MB
memory_limit_entry = Entry(mainwin, textvariable=memory_limit_var, width=10)
memory_limit_entry.grid(row=5, column=17, padx=(0, 20), pady=(10, 0), sticky='w')

Label(mainwin, text="Number of Threads: ").grid(row=4, column=18, columnspan=5)
spin = Spinbox(mainwin, from_=1, to=16)
spin.grid(row=4, column=22)

var1 = tk.IntVar()
checkbox = tk.Checkbutton(mainwin, text='Use Test Case Instead', variable=var1, onvalue=1, offvalue=0)
checkbox.grid(row=5, column=22)

# Passed tests counter
passed_count_var = StringVar(value="Tests Passed: 0")  # Added
passed_label = Label(mainwin, textvariable=passed_count_var, font=bold_font, foreground='green')  # Styled label
passed_label.grid(row=6, column=1, padx=(20,0), pady=(10,0), sticky='w')  # Placed near the top

# Initialize the log text area at startup to prevent AttributeError
tex = Text(mainwin, height=10, width=120, font=('Courier', 10))  # Set monospaced font
tex.grid(row=7, padx=20, pady=(0, 20), column=0, columnspan=30)
tex.configure(state='disabled')

# Define a red tag for highlighting errors and mismatches
tex.tag_configure('red', foreground='red')

# Initialize global variables
execute = None
exit_btn = None
continue_executing = True
workers = []
test_num = 1

mutex = threading.Lock()
available = threading.Lock()

# Create 'usable' directory if it doesn't exist
if not os.path.exists("usable"):
    os.mkdir("usable")

def on_closing():
    if messagebox.askokcancel("Quit", "Do you want to quit?"):
        stop_all_workers()
        if os.path.exists("usable"):
            shutil.rmtree("usable")
        mainwin.destroy()

def printf(input_text, end_char="\n"):
    """
    Enqueue messages to be printed in the GUI with timestamps.
    """
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    msg_queue.put((f"[{timestamp}] {input_text}", end_char))

def passed():
    """
    Enqueue a message indicating a passed test.
    """
    global test_num
    with mutex:
        test_num += 1
        msg_queue.put(("PASSED", ""))  # Changed to enqueue 'PASSED' message without logging

def stop_all_workers():
    """
    Signal all worker threads to stop and wait for them to finish.
    """
    global continue_executing, workers, test_num
    continue_executing = False
    for worker in workers.copy():
        worker.join(timeout=1)
    workers.clear()
    test_num = 1
    continue_executing = True
    # Re-enable GUI elements through queue
    msg_queue.put(("ENABLE_GUI", ""))

def reset_gui_after_failure():
    """
    Reset GUI elements after a hack failure to allow re-execution.
    """
    # Re-enable GUI elements
    checkbox.configure(state='normal')
    execute.configure(state='normal')
    spin.configure(state='normal')
    source.configure(state='normal')
    compare.configure(state='normal')
    generator.configure(state='normal')
    exit_btn.configure(state='disabled')
    # Re-enable time and memory limit entries
    time_limit_entry.configure(state='normal')
    memory_limit_entry.configure(state='normal')

def rewrite(path, data):
    """
    Write data to a file at the specified path.
    """
    with open(path, "w") as raw:
        raw.write(data)

def parse_cpp_inputs(cpp_code):
    """
    Parses C++ code to extract input variables and their types.
    """
    # Regex to capture variable declarations (supports basic types)
    var_decl_regex = re.compile(r'\b(int|long_long|long|double|float|string)\s+(\w+);')

    # Regex to capture cin >> lines
    cin_regex = re.compile(r'cin\s*>>\s*(.+?);')

    # Map to store variable types
    var_types = {}

    # Find all variable declarations
    for match in var_decl_regex.finditer(cpp_code):
        var_type = match.group(1)
        var_name = match.group(2)
        var_types[var_name] = var_type

    # Find all cin >> lines and extract variables
    inputs = []
    for match in cin_regex.finditer(cpp_code):
        vars_part = match.group(1)
        # Split by '>>' and possible multiple variables
        vars = [v.strip() for v in vars_part.split('>>')]
        inputs.extend(vars)

    # Remove duplicates while preserving order
    seen = set()
    inputs = [x for x in inputs if not (x in seen or seen.add(x))]

    # Get types for input variables
    input_vars = []
    for var in inputs:
        var_type = var_types.get(var, 'int')  # default to int if type not found
        input_vars.append((var, var_type))

    return input_vars

def parse_python_inputs(python_code):
    """
    Parses Python code to extract input variables and their types using AST.
    Assumes input is read using int(input()), float(input()), etc.
    """
    class InputVisitor(ast.NodeVisitor):
        def __init__(self):
            self.inputs = []  # List of tuples: (variable_name, type)
        
        def visit_Assign(self, node):
            # Look for assignments like x = int(input())
            if isinstance(node.value, ast.Call):
                func = node.value.func
                if isinstance(func, ast.Name) and func.id in ['int', 'float', 'str']:
                    # Assuming single target assignment
                    if len(node.targets) == 1 and isinstance(node.targets[0], ast.Name):
                        var_name = node.targets[0].id
                        var_type = func.id
                        self.inputs.append((var_name, var_type))
            self.generic_visit(node)
        
        def visit_AugAssign(self, node):
            # Handle augmented assignments if necessary
            self.generic_visit(node)

    try:
        tree = ast.parse(python_code)
        visitor = InputVisitor()
        visitor.visit(tree)
        return visitor.inputs
    except Exception as e:
        return []  # In case of parsing errors

def generate_test_case_generator_cpp(input_vars, test_cases=10):
    """
    Generates Python test case generator code based on C++ input variables and their types.
    """
    generator_code = """import random

def generate_test_input():
    t = random.randint(1, {test_cases})
    print(t)
    for _ in range(t):
""".format(test_cases=test_cases)

    for var, var_type in input_vars:
        indent = ' ' * 8
        if var_type == 'int':
            generator_code += f"{indent}{var} = random.randint(1, 10**9)\n"
        elif var_type in ['long_long', 'long']:
            generator_code += f"{indent}{var} = random.randint(1, 10**18)\n"
        elif var_type in ['double', 'float']:
            generator_code += f"{indent}{var} = random.uniform(1.0, 10**9)\n"
        elif var_type == 'string':
            generator_code += f"{indent}{var} = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz', k=10))\n"
        else:
            generator_code += f"{indent}{var} = 0  # Unknown type: {var_type}\n"
        generator_code += f"{indent}print({var})\n"
    
    generator_code += "\n# Example usage:\n# generate_test_input()\n"
    
    return generator_code

def generate_test_case_generator_python(input_vars, test_cases=10):
    """
    Generates Python test case generator code based on Python input variables and their types.
    """
    generator_code = """import random

def generate_test_input():
    t = random.randint(1, {test_cases})
    print(t)
    for _ in range(t):
""".format(test_cases=test_cases)

    for var, var_type in input_vars:
        indent = ' ' * 8
        if var_type == 'int':
            generator_code += f"{indent}{var} = random.randint(1, 10**9)\n"
        elif var_type == 'float':
            generator_code += f"{indent}{var} = random.uniform(1.0, 10**9)\n"
        elif var_type == 'str':
            generator_code += f"{indent}{var} = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz', k=10))\n"
        else:
            generator_code += f"{indent}{var} = 0  # Unknown type: {var_type}\n"
        generator_code += f"{indent}print({var})\n"
    
    generator_code += "\n# Example usage:\n# generate_test_input()\n"
    
    return generator_code

def auto_generate_generator():
    """
    Automatically generates the test case generator based on the source solution code.
    Detects whether the source is C++ or Python and parses accordingly.
    Allows further editing after generation.
    """
    source_language = source_lang_var.get()
    source_code = source.get(1.0, END)
    
    if source_language == "C++":
        input_vars = parse_cpp_inputs(source_code)
        if not input_vars:
            printf("No input variables found. Please ensure your C++ code contains input reading via 'cin'.", "")
            return
        generator_code = generate_test_case_generator_cpp(input_vars, test_cases=10)
    elif source_language == "Python":
        input_vars = parse_python_inputs(source_code)
        if not input_vars:
            printf("No input variables found. Please ensure your Python code contains input reading using int(input()), float(input()), or str(input()).", "")
            return
        generator_code = generate_test_case_generator_python(input_vars, test_cases=10)
    else:
        printf("Unsupported source language selected.", "")
        return
    
    # Populate the generator text area with the generated code
    generator.configure(state='normal')
    generator.delete(1.0, END)
    generator.insert(END, generator_code)
    generator.configure(state='normal')  # Keep it editable after auto-generation
    
    # Optionally, write the generator code to the 'usable' directory
    if source_language == "C++":
        rewrite("usable/generator.py", generator_code)
    elif source_language == "Python":
        rewrite("usable/generator.py", generator_code)
    
    printf("Test case generator auto-generated successfully.", "")

def execut(name, input_data, language="C++", num=0):
    """
    Execute a program (Python or C++) and return its output and errors.
    Enforces time and memory limits based on user input.
    """
    # Retrieve time and memory limits from the GUI
    try:
        time_limit = float(time_limit_var.get())
        if time_limit <= 0:
            raise ValueError
    except ValueError:
        printf("Invalid Time Limit. Please enter a positive number.", "")
        return "", "Invalid Time Limit."

    try:
        memory_limit_mb = int(memory_limit_var.get())
        if memory_limit_mb <= 0:
            raise ValueError
    except ValueError:
        printf("Invalid Memory Limit. Please enter a positive integer.", "")
        return "", "Invalid Memory Limit."
    
    # Convert memory limit to bytes
    memory_limit_bytes = memory_limit_mb * 1024 * 1024
    
    if language == "Python":
        # Execute Python script using the current Python interpreter
        command = [sys.executable, f'{name}.py']
        try:
            if input_data:
                # Ensure input_data is a string
                if isinstance(input_data, bytes):
                    try:
                        input_data = input_data.decode('utf-8')
                    except UnicodeDecodeError as e:
                        printf(f"RED: Failed to decode input data for Python execution: {e}\n", "")
                        return "", "Failed to decode input data."
                
                p = sub.Popen(
                    command,
                    stdout=sub.PIPE,
                    stderr=sub.PIPE,
                    cwd='usable',
                    stdin=sub.PIPE,
                    text=True  # Ensures input and output are strings
                )
                output, errors = p.communicate(input=input_data, timeout=time_limit)
            else:
                p = sub.Popen(
                    command,
                    stdout=sub.PIPE,
                    stderr=sub.PIPE,
                    cwd='usable'
                )
                output, errors = p.communicate(timeout=time_limit)
        except sub.TimeoutExpired:
            p.kill()
            output, errors = p.communicate()
            printf(f"RED: Time Limit Exceeded for {name}.py\n", "")
            return "", "Time Limit Exceeded."
        except FileNotFoundError:
            msg_queue.put(("RED: Python interpreter not found.", ""))
            return "", "Python interpreter not found."
        return output, errors

    elif language == "C++":
        # Prepare input file
        input_file = f"usable/input{num}.txt"
        rewrite(input_file, input_data)  # Ensure input_data is str

        # Execute C++ program
        executable = f"./{name}" if os.name != 'nt' else f"{name}.exe"
        command = [executable]
    
        try:
            if os.name != 'nt':
                # Define a pre-exec function to set memory limits (Unix-based systems)
                def limit_resources():
                    try:
                        # Set the maximum address space (virtual memory)
                        resource.setrlimit(resource.RLIMIT_AS, (memory_limit_bytes, memory_limit_bytes))
                    except Exception as e:
                        printf(f"RED: Failed to set memory limit: {e}\n", "")
    
                # Added '-std=c++11' to compilation step
                p = sub.Popen(
                    command,
                    stdout=sub.PIPE,
                    stderr=sub.PIPE,
                    cwd='usable',
                    stdin=open(input_file, 'r'),
                    preexec_fn=limit_resources
                )
            else:
                # Memory limit enforcement is not straightforward on Windows
                # Skipping memory limiting on Windows
                p = sub.Popen(
                    command,
                    stdout=sub.PIPE,
                    stderr=sub.PIPE,
                    cwd='usable',
                    stdin=open(input_file, 'r')
                )
            
            output, errors = p.communicate(timeout=time_limit)
        except sub.TimeoutExpired:
            p.kill()
            output, errors = p.communicate()
            printf(f"RED: Time Limit Exceeded for {name}\n", "")
            return "", "Time Limit Exceeded."
        except FileNotFoundError:
            msg_queue.put((f"RED: Executable {name} not found in 'usable' directory.", ""))
            return "", "Executable not found."
        except Exception as e:
            msg_queue.put((f"RED: Error executing {name}: {e}", ""))
            return "", f"Error executing {name}: {e}"
        
        # If on Unix, check for memory usage exceeding the limit
        if os.name != 'nt':
            # Since RLIMIT_AS was set, if memory exceeds, the process should be killed
            # We can check the return code to see if it was killed due to memory
            if p.returncode != 0 and 'killed' in str(errors).lower():
                printf(f"RED: Memory Limit Exceeded for {name}\n", "")
                return "", "Memory Limit Exceeded."
        
        return output.decode('utf-8'), errors.decode('utf-8')

    else:
        msg_queue.put((f"RED:Unknown language: {language}", ""))
        return "", f"Unknown language: {language}"

def compares(input_text, pp):
    """
    Compare the outputs of the source and hack solutions.
    """
    # Ensure input_text is a string
    if isinstance(input_text, bytes):
        try:
            input_text = input_text.decode('utf-8')
        except UnicodeDecodeError:
            printf("RED: Failed to decode input data.\n", "")
            stop_all_workers()
            msg_queue.put(("ENABLE_GUI", ""))
            return False

    # Retrieve language selections
    source_language = source_lang_var.get()
    compare_language = compare_lang_var.get()
    
    # Execute Source Solution
    a_output, a_errors = execut(f'source{pp}', input_text, language=source_language)
    # Execute Compare Solution
    b_output, b_errors = execut(f'compare{pp}', input_text, language=compare_language)

    if a_errors.strip():
        msg_queue.put((f"RED:Runtime Error on Source!\nInput:\n{input_text}\n\nError:\n{a_errors}\nExiting Thread..\n", ""))
        stop_all_workers()
        msg_queue.put(("ENABLE_GUI", ""))
        return False
    if b_errors.strip():
        msg_queue.put((f"RED:Runtime Error on Hack!\nInput:\n{input_text}\n\nError:\n{b_errors}\nExiting Thread..\n", ""))
        stop_all_workers()
        msg_queue.put(("ENABLE_GUI", ""))
        return False

    # Compare outputs
    if a_output.strip() != b_output.strip():
        # Format message as a table
        # Adjust column widths as needed
        table = (
            "---------------------------------------------------------------------\n"
            f"Input:\n{input_text}\n"
            "---------------------------------------------------------------------\n"
            f"{'Your Output':<20} | {'Hack Output'}\n"
            "---------------------------------------------------------------------\n"
            f"{a_output.strip():<20} | {b_output.strip()}\n"
            "---------------------------------------------------------------------\n"
            "Exiting Thread..\n"
        )
        msg_queue.put((table, "red"))
        stop_all_workers()
        msg_queue.put(("ENABLE_GUI", ""))
        return False
    
    # If outputs match, only increment the counter
    passed()
    return True

def run(pp, single=False):
    """
    Worker thread function to compile and execute code.
    """
    global continue_executing
    with available:
        if not continue_executing:
            return

        # Retrieve language selections
        source_language = source_lang_var.get()
        compare_language = compare_lang_var.get()

        # Handle Source Language
        if source_language == "C++":
            # Added '-std=c++11' to compilation step
            compile_source = ['clang++', '-std=c++11', 'source.cpp', '-o', f'source{pp}']
            try:
                p = sub.Popen(compile_source, stdout=sub.PIPE, stderr=sub.PIPE, cwd='usable')
                output, errors = p.communicate()
                errors = errors.decode('utf-8')
                if errors.strip():
                    msg_queue.put(("RED:Source Compilation Error!\nExiting..\n", ""))
                    stop_all_workers()
                    msg_queue.put(("ENABLE_GUI", ""))
                    return
                # Set executable permissions (Unix-based systems)
                if os.name != 'nt':
                    os.chmod(f'usable/source{pp}', 0o755)
            except FileNotFoundError:
                msg_queue.put(("RED:clang++ not found. Please ensure it is installed and added to PATH.\n", ""))
                stop_all_workers()
                msg_queue.put(("ENABLE_GUI", ""))
                return
        elif source_language == "Python":
            # Write per-thread source Python file
            source_code = source.get(1.0, END)
            rewrite(f"usable/source{pp}.py", source_code)
        else:
            msg_queue.put((f"RED:Unsupported source language: {source_language}\n", ""))
            stop_all_workers()
            msg_queue.put(("ENABLE_GUI", ""))
            return

        # Handle Compare Language
        if compare_language == "C++":
            # Added '-std=c++11' to compilation step
            compile_compare = ['clang++', '-std=c++11', 'compare.cpp', '-o', f'compare{pp}']
            try:
                p = sub.Popen(compile_compare, stdout=sub.PIPE, stderr=subprocess.PIPE, cwd='usable')
                output, errors = p.communicate()
                errors = errors.decode('utf-8')
                if errors.strip():
                    msg_queue.put(("RED:Solution to Hack Compilation Error!\nExiting..\n", ""))
                    stop_all_workers()
                    msg_queue.put(("ENABLE_GUI", ""))
                    return
                # Set executable permissions (Unix-based systems)
                if os.name != 'nt':
                    os.chmod(f'usable/compare{pp}', 0o755)
            except FileNotFoundError:
                msg_queue.put(("RED:clang++ not found. Please ensure it is installed and added to PATH.\n", ""))
                stop_all_workers()
                msg_queue.put(("ENABLE_GUI", ""))
                return
        elif compare_language == "Python":
            # Write per-thread compare Python file
            compare_code = compare.get(1.0, END)
            rewrite(f"usable/compare{pp}.py", compare_code)
        else:
            msg_queue.put((f"RED:Unsupported compare language: {compare_language}\n", ""))
            stop_all_workers()
            msg_queue.put(("ENABLE_GUI", ""))
            return

        # If not running in single mode, update the generator script
        if not single:
            # Ensure generator script is thread-specific
            generator_code = generator.get(1.0, END)
            rewrite(f"usable/generator{pp}.py", generator_code)

    while continue_executing:
        if single:
            # Use the provided test case instead of generating one
            test_case = generator.get(1.0, END).strip()
            if not test_case:
                msg_queue.put(("RED:No test case provided.\nExiting..\n", ""))
                stop_all_workers()
                msg_queue.put(("ENABLE_GUI", ""))
                return
            result = compares(test_case, pp)
            if result:
                # After a single test case, stop execution to allow user modification
                stop_all_workers()
            return

        # Execute the generator to produce a test case
        generate_output, generate_errors = execut(f'generator{pp}', "", language="Python")
        if generate_errors.strip():
            msg_queue.put((f"RED:Generator Error!\n{generate_errors}\nExiting..\n", ""))
            stop_all_workers()
            msg_queue.put(("ENABLE_GUI", ""))
            return

        # Compare the outputs of the source and hack solutions
        result = compares(generate_output, pp)
        if result:
            # Continue to generate and test the next test case
            continue
        else:
            # If mismatch, stop and allow user to edit generator
            return

def printer():
    """
    Start the execution process by compiling code and initiating worker threads.
    """
    global execute, exit_btn

    # Clear previous logs
    tex.configure(state='normal')
    tex.delete(1.0, END)
    tex.configure(state='disabled')
    
    # Reset passed tests counter
    passed_count_var.set("Tests Passed: 0")
    global test_num
    test_num = 0  # Reset test_num

    # Write the source and compare code to the 'usable' directory based on selected languages
    source_language = source_lang_var.get()
    compare_language = compare_lang_var.get()

    if source_language == "C++":
        rewrite("usable/source.cpp", source.get(1.0, END))
    elif source_language == "Python":
        # Initialize with source0.py for the first thread
        rewrite("usable/source0.py", source.get(1.0, END))
    else:
        # This should not happen as options are limited to C++ and Python
        printf("Unsupported Source Language Selected.", "")
        return

    if compare_language == "C++":
        rewrite("usable/compare.cpp", compare.get(1.0, END))
    elif compare_language == "Python":
        # Initialize with compare0.py for the first thread
        rewrite("usable/compare0.py", compare.get(1.0, END))
    else:
        # This should not happen as options are limited to C++ and Python
        printf("Unsupported Compare Language Selected.", "")
        return

    # Disable GUI elements during execution
    checkbox.configure(state='disabled')
    execute.configure(state='disabled')
    spin.configure(state='disabled')
    source.configure(state='disabled')
    compare.configure(state='disabled')
    generator.configure(state='disabled')
    exit_btn.configure(state='normal')
    # Disable time and memory limit entries during execution
    time_limit_entry.configure(state='disabled')
    memory_limit_entry.configure(state='disabled')

    if var1.get() == 1:
        # Single-threaded mode using provided test case
        p = threading.Thread(target=run, args=(str(0), True), name='0', daemon=True)
        p.start()
        workers.append(p)
        printf("Started Thread: 1")
    else:
        # Multi-threaded mode with generated test cases
        for i in range(int(spin.get())):
            p = threading.Thread(target=run, args=(str(i),), name=str(i), daemon=True)
            p.start()
            workers.append(p)
            printf(f"Started Thread: {i + 1}")
    return

def process_queue():
    """
    Periodically check the queue and update the GUI.
    """
    try:
        while True:
            message, color = msg_queue.get_nowait()
            if color:
                # Insert text with the specified color
                tex.configure(state='normal')
                tex.insert(tk.END, message + "\n", 'red')  # Assuming color 'red' is for mismatches and errors
                tex.see(tk.END)
                tex.configure(state='disabled')
            elif message.startswith("RED:"):
                # Remove the 'RED:' prefix and insert in red
                clean_message = message[4:]
                tex.configure(state='normal')
                tex.insert(tk.END, clean_message + "\n", 'red')
                tex.see(tk.END)
                tex.configure(state='disabled')
            elif message.startswith("WARNING:"):
                # Handle warnings (e.g., memory limit not enforced on Windows)
                clean_message = message[len("WARNING:"):]
                tex.configure(state='normal')
                tex.insert(tk.END, clean_message + "\n", 'red')
                tex.see(tk.END)
                tex.configure(state='disabled')
            elif message == "ENABLE_GUI":
                # Re-enable GUI elements
                reset_gui_after_failure()
                printf("Ready for new execution.", "")
            elif message == "PASSED":
                # Increment passed count
                current_count = passed_count_var.get().split(": ")[1]
                try:
                    new_count = int(current_count) + 1
                except ValueError:
                    new_count = 1
                passed_count_var.set(f"Tests Passed: {new_count}")
            else:
                # Insert regular messages without color
                tex.configure(state='normal')
                tex.insert(tk.END, message + "\n")
                tex.see(tk.END)
                tex.configure(state='disabled')
    except queue.Empty:
        pass
    finally:
        mainwin.after(100, process_queue)  # Check the queue every 100 ms

# Initialize Execute, Stop, and Auto-Generate Generator buttons
execute = Button(mainwin, text="Execute", command=printer)
execute.grid(row=6, column=18, columnspan=5, sticky="EW", pady=(0, 25))

exit_btn = Button(mainwin, text="Stop", command=stop_all_workers)
exit_btn.grid(row=6, column=25, columnspan=3, sticky="EW", pady=(0, 25))
exit_btn.configure(state='disabled')

# Initialize Auto-Generate Generator button
auto_gen_btn = Button(mainwin, text="Auto-Generate Generator", command=auto_generate_generator)
auto_gen_btn.grid(row=3, column=16, columnspan=15, sticky="EW", padx=20, pady=(10, 0))

# Handle window closing event
mainwin.protocol("WM_DELETE_WINDOW", on_closing)

# Start processing the queue
mainwin.after(100, process_queue)

# Start the Tkinter event loop
mainwin.mainloop(). error : 2024-11-15 01:02:14.661 Python[79244:514098] +[IMKClient subclass]: chose IMKClient_Legacy
2024-11-15 01:02:14.661 Python[79244:514098] +[IMKInputSession subclass]: chose IMKInputSession_Legacy
Exception in thread 0:
Traceback (most recent call last):
  File "/opt/Homebrew/Cellar/python@3.13/3.13.0_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/threading.py", line 1041, in _bootstrap_inner
    self.run()
    ~~~~~~~~^^
  File "/opt/Homebrew/Cellar/python@3.13/3.13.0_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/threading.py", line 992, in run
    self._target(*self._args, **self._kwargs)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/nghiaphan/Desktop/Hack_Assistant/main.py", line 576, in run
    p = sub.Popen(compile_compare, stdout=sub.PIPE, stderr=subprocess.PIPE, cwd='usable')
                                                           ^^^^^^^^^^
NameError: name 'subprocess' is not defined. Did you forget to import 'subprocess'?