import subprocess
import sys
import tkinter as tk
import time
import os

# Change that for more or less games
GAMES = 10

lines = [
    [1, 2, 3], [4, 5, 6], [7, 8, 9],  
    [1, 4, 7], [2, 5, 8], [3, 6, 9], 
    [1, 5, 9], [3, 5, 7]             
]

def any_line(occupied):
    for [x, y, z] in lines:
        if x in occupied and y in occupied and z in occupied:
            return True
    return False

class GameGUI:


    def __init__(self, root, move_callback, start_callback):
        self.root = root
        self.move_callback = move_callback
        self.start_callback = start_callback
        self.buttons = {}
        self.status = None
        self.start_btn = None
        self.create_board()

    def create_board(self):
        for widget in self.root.winfo_children():
            widget.destroy()

        self.buttons.clear()

        self.status = tk.Label(self.root, text="", font=("Helvetica", 18))
        self.status.grid(row=0, column=0, columnspan=3, pady=(10, 5))

        for i in range(3):
            for j in range(3):
                idx = 3 * i + j + 1
                btn = tk.Button(
                    self.root,
                    text=str(idx),
                    width=8,
                    height=4,
                    font=("Helvetica", 28),
                    command=lambda idx=idx: self.move_callback(idx),
                    bg="white"
                )
                btn.grid(row=i + 1, column=j, padx=5, pady=5)
                self.buttons[idx] = btn

        self.start_btn = tk.Button(
            self.root,
            text="Start Game",
            font=("Helvetica", 16),
            command=self.start_game
        )
        self.start_btn.grid(row=4, column=0, columnspan=3, pady=(10, 5))

        self.root.update()

    def start_game(self):
        self.start_btn.grid_remove()
        self.status.config(text="")
        self.start_callback()

    def show_user_prompt(self):
        self.status.config(text="Your move!")

    def hide_user_prompt(self):
        self.status.config(text="")

    def reset_board(self):
        for idx, btn in self.buttons.items():
            btn.config(text=str(idx), state=tk.NORMAL, bg="white")

    def disable_all(self):
        for btn in self.buttons.values():
            btn.config(state=tk.DISABLED)

    def mark(self, idx):
        self.buttons[idx].config(text='X', state=tk.DISABLED)

    def highlight_line(self, line, color):
        for idx in line:
            self.buttons[idx].config(bg=color)

def main():
    if len(sys.argv) < 2:
        print(f"Usage: python3 {sys.argv[0]} <filename>\nExamples:\n\tpython3 {sys.argv[0]} solution_exe\n\tpython3 {sys.argv[0]} solution.py")
        sys.exit(1)

    executable = sys.argv[1]

    if not os.path.exists(executable):
        print(f"ERROR: Cannot find {executable}")
        sys.exit(1)

    root = tk.Tk()
    root.title("3x3 Game - Click to Play")

    occupied = set()
    free = set(range(1, 10))
    waiting_for_user = False
    user_move_ready = False
    user_last_move = None
    next_game_ready = False
    user_won = False

    def start_callback():
        nonlocal next_game_ready
        gui.reset_board()
        next_game_ready = True

    def user_move(idx):
        nonlocal waiting_for_user, user_move_ready, user_last_move, user_won
        if idx in occupied or not waiting_for_user:
            return
        print(f"User played: {idx}")
        user_last_move = idx
        occupied.add(idx)
        free.remove(idx)
        gui.mark(idx)
        waiting_for_user = False
        user_move_ready = True
        for line in lines:
            if all(pos in occupied for pos in line):
                gui.status.config(text="You win! AI lost.")
                gui.highlight_line(line, 'red')
                print(f"User completed line: {line}")
                user_won = True


    gui = GameGUI(root, user_move, start_callback)
    
    def run_game():
        nonlocal occupied, free, waiting_for_user, user_move_ready, next_game_ready

        try:
            commands = ['./' + executable]
            if executable.endswith('.py'):
                commands = ['python3', executable]

            proc = subprocess.Popen(
                commands,
                stdin=subprocess.PIPE,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True
            )

            print(f"Sending number of games: {GAMES}")
            proc.stdin.write(str(GAMES) + '\n')
            proc.stdin.flush()

            for game_index in range(GAMES):

                next_game_ready = False
                gui.status.config(text="Click starting button")
                while not next_game_ready:
                    root.update()
                    time.sleep(0.1)
                
                gui.start_btn.grid_remove()
                # gui.status.config(text="You win! AI lost.")
                
                print(f"\nStarting game #{game_index + 1}")
                gui.reset_board()
                occupied = set()
                free = set(range(1, 10))

                while True:
                    response = proc.stdout.readline().strip()
                    if response == '':
                        raise Exception("No response or process ended communication.")
                    move = int(response)
                    print(f"AI played: {move}")
                    if move not in free:
                        raise Exception("AI move is incorrect")
                    occupied.add(move)
                    free.remove(move)
                    gui.mark(move)
                    ai_won = False
                    for line in lines:
                        if all(pos in occupied for pos in line):
                            gui.status.config(text="You lost! AI won.")
                            gui.highlight_line(line, 'green')
                            print(f"AI completed line: {line}")
                            ai_won = True
                            break
                    if ai_won:
                        break
                    waiting_for_user = True
                    gui.status.config(text="Choose square")
                    while not user_move_ready:
                        root.update()
                    user_move_ready = False

                    if user_won:
                        break


                    proc.stdin.write(str(user_last_move) + '\n')
                    proc.stdin.flush()
                
                if game_index == GAMES - 1 or user_won:
                    gui.start_btn.config(text="Quit", command=root.quit)
                else:
                    gui.start_btn.config(text="Start Game", command=gui.start_game)
                gui.start_btn.grid()
                if user_won:
                    break

        except Exception as e:
            print(f"\nFATAL ERROR: {e}")
            gui.disable_all()

        finally:
            proc.stdin.close()
            proc.stdout.close()
            proc.terminate()
            proc.wait()
    
    root.after(100, run_game)
    root.mainloop()

if __name__ == "__main__":
    main()