"""
Halving Game -- GUI version.

The game logic (HalvingGame) and the minimax search (minimaxPolicy) are
UNCHANGED from the original console version -- same states, same actions,
same recursion. What changed is purely *display and interaction*:
  - a Tkinter window shows the state instead of console print()/input()
  - you can choose "Human vs AI" or "Human vs Human" before starting
  - every single move is logged as: who moved, what action they picked,
    and the resulting state -- so the whole game is readable as a trace
    afterwards.
"""

import tkinter as tk
from tkinter import messagebox, scrolledtext


############################################################
# Modeling  (unchanged from the original game.py)

class HalvingGame(object):
    def __init__(self, N):
        self.N = N

    # state = (player, number)
    def startState(self):
        return (+1, self.N)

    def actions(self, state):
        player, number = state
        return ['-', '/']

    def succ(self, state, action):
        player, number = state
        if action == '-':
            return (-player, number - 1)
        elif action == '/':
            return (-player, number // 2)
        assert False

    def isEnd(self, state):
        player, number = state
        return number == 0

    def utility(self, state):
        player, number = state
        assert self.isEnd(state)
        return player * float('inf')

    def player(self, state):
        player, number = state
        return player


############################################################
# Policies  (same recursion as the original; only the reporting mechanism
# is parameterized so it can write into the GUI log instead of stdout)

def simplePolicy(game, state):
    action = '-'
    return action


def minimaxPolicy(game, state, log=None):
    def recurse(state):
        # Return (utility of that state, action that achieves that utility)
        if game.isEnd(state):
            return (game.utility(state), None)
        # List of (utility of succ, action leading to that succ)
        candidates = [
            (recurse(game.succ(state, action))[0], action)
            for action in game.actions(state)
        ]
        player = game.player(state)
        if player == +1:
            return max(candidates)
        elif player == -1:
            return min(candidates)
        assert False

    utility, action = recurse(state)
    if log is not None:
        log('  [minimax search chose action {!r} with value {}]'.format(action, utility))
    return action


############################################################
# GUI

ACTION_NAME = {
    '-': "Decrement (-1)",
    '/': "Halve (//2)",
}


class HalvingGameGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("Halving Game")
        self.root.resizable(False, False)

        self.game = None
        self.state = None
        self.human_players = None   # set of players (+1 / -1) controlled by humans
        self.move_number = 0

        # ---------- Setup: starting number ----------
        setup = tk.Frame(root, padx=12)
        setup.pack(fill='x', pady=(12, 4))

        tk.Label(setup, text="Starting number N:").pack(side='left')
        self.n_entry = tk.Entry(setup, width=8)
        self.n_entry.insert(0, "6")
        self.n_entry.pack(side='left', padx=6)

        self.start_btn = tk.Button(setup, text="Start Game", command=self.start_game)
        self.start_btn.pack(side='left', padx=6)

        # ---------- Setup: game mode ----------
        mode_frame = tk.Frame(root, padx=12)
        mode_frame.pack(fill='x', pady=(0, 8))

        tk.Label(mode_frame, text="Mode:").pack(side='left')
        self.mode_var = tk.StringVar(value='human_vs_ai')
        self.mode_human_ai = tk.Radiobutton(
            mode_frame, text="Human (+1) vs AI (-1)",
            variable=self.mode_var, value='human_vs_ai'
        )
        self.mode_human_ai.pack(side='left', padx=6)
        self.mode_human_human = tk.Radiobutton(
            mode_frame, text="Human vs Human",
            variable=self.mode_var, value='human_vs_human'
        )
        self.mode_human_human.pack(side='left', padx=6)

        # ---------- Current state ----------
        self.state_label = tk.Label(
            root, text="Choose a mode, enter N, and click Start Game.",
            font=('Helvetica', 16, 'bold'), pady=10, justify='center'
        )
        self.state_label.pack(fill='x', padx=12)

        # ---------- Action buttons (whoever's turn it is, if human) ----------
        actions_frame = tk.Frame(root, pady=6)
        actions_frame.pack()

        self.minus_btn = tk.Button(
            actions_frame, text="Decrement  (currNumber - 1)",
            width=26, command=lambda: self.human_move('-')
        )
        self.minus_btn.pack(side='left', padx=6)

        self.half_btn = tk.Button(
            actions_frame, text="Halve  (currNumber // 2)",
            width=26, command=lambda: self.human_move('/')
        )
        self.half_btn.pack(side='left', padx=6)

        self.set_human_buttons(False)

        # ---------- Move log ----------
        tk.Label(root, text="Move log:").pack(anchor='w', padx=12)
        self.log_box = scrolledtext.ScrolledText(root, width=70, height=16, state='disabled')
        self.log_box.pack(padx=12, pady=(0, 12))

    # ----- helpers -----

    def log(self, msg):
        self.log_box.configure(state='normal')
        self.log_box.insert('end', msg + '\n')
        self.log_box.see('end')
        self.log_box.configure(state='disabled')

    def set_human_buttons(self, enabled):
        state = 'normal' if enabled else 'disabled'
        self.minus_btn.configure(state=state)
        self.half_btn.configure(state=state)

    def set_mode_controls(self, enabled):
        state = 'normal' if enabled else 'disabled'
        self.mode_human_ai.configure(state=state)
        self.mode_human_human.configure(state=state)
        self.n_entry.configure(state=state)

    def mover_label(self, player):
        """'Human (Player +1)' or 'AI (Player -1)', depending on current mode."""
        who = "Human" if player in self.human_players else "AI"
        sign = "+1" if player == +1 else "-1"
        return "{} (Player {})".format(who, sign)

    # ----- game flow -----

    def start_game(self):
        try:
            N = int(self.n_entry.get())
            if N < 0:
                raise ValueError
        except ValueError:
            messagebox.showerror("Invalid input", "Please enter a non-negative integer for N.")
            return

        if self.mode_var.get() == 'human_vs_ai':
            self.human_players = {+1}
        else:
            self.human_players = {+1, -1}

        self.game = HalvingGame(N)
        self.state = self.game.startState()
        self.move_number = 0

        self.log_box.configure(state='normal')
        self.log_box.delete('1.0', 'end')
        self.log_box.configure(state='disabled')

        mode_desc = "Human vs AI" if self.human_players == {+1} else "Human vs Human"
        self.log("=== New game: N = {}, mode = {} ===".format(N, mode_desc))
        self.log("Initial state: {}".format(self.state))

        self.set_mode_controls(False)
        self.refresh()

    def refresh(self):
        if self.game is None:
            return

        if self.game.isEnd(self.state):
            self.set_human_buttons(False)
            self.set_mode_controls(True)
            utility = self.game.utility(self.state)
            winner = self.mover_label(+1) if utility > 0 else self.mover_label(-1)
            self.state_label.configure(
                text="Game over! currNumber = 0.\nWinner: {}".format(winner)
            )
            self.log("=== Game over. Final state: {}. Utility = {}. Winner: {} ===".format(
                self.state, utility, winner))
            return

        player, number = self.state
        mover = self.mover_label(player)
        self.state_label.configure(text="currNumber = {}\nTurn: {}".format(number, mover))

        if player in self.human_players:
            self.set_human_buttons(True)
        else:
            self.set_human_buttons(False)
            # small delay so the "AI's turn" label is visible before it moves
            self.root.after(400, self.ai_move)

    def human_move(self, action):
        state_before = self.state
        player = self.game.player(state_before)
        mover = self.mover_label(player)

        self.state = self.game.succ(state_before, action)
        self.move_number += 1
        self.log("Move {}: {} at state {} picks '{}' ({}) -> new state {}".format(
            self.move_number, mover, state_before, action, ACTION_NAME[action], self.state))

        self.refresh()

    def ai_move(self):
        state_before = self.state
        player = self.game.player(state_before)
        mover = self.mover_label(player)

        action = minimaxPolicy(self.game, state_before, log=self.log)

        self.state = self.game.succ(state_before, action)
        self.move_number += 1
        self.log("Move {}: {} at state {} picks '{}' ({}) -> new state {}".format(
            self.move_number, mover, state_before, action, ACTION_NAME[action], self.state))

        self.refresh()


############################################################

if __name__ == '__main__':
    root = tk.Tk()
    app = HalvingGameGUI(root)
    root.mainloop()
