import tkinter as tk
from tkinter import messagebox


# We can make a "virtual" game board as a list of lists. The list contains 3 other lists (rows).
# Each row list contains 3 string values representing spaces. The spaces will be populated with
# either "X" or "O", but we start with empty strings to denote an empty space.
#
# We can access a space on the board using row and column indexes. For example, the top-left space
# is `board[0][0]`, the center space is `board[1][1]`, and the bottom-right space is `board[2][2]`.
board = [
    ["", "", ""],
    ["", "", ""],
    ["", "", ""]
]

# We'll also store the buttons in a similiar way. This list will be auto-populated by make_button()
buttons = []


#
# Game Window Setup
#

# Now we create our window and give it a title. We'll store this window in a variable called
# "game", so we can access it later.
game = tk.Tk()
game.title("Tic Tac Toe")

# Add a message area (to display text to the user). Use `message.set(text)` to change the message.
message = tk.StringVar()
message_area = tk.Label(game, textvariable=message, font="Times 20 bold", pady=20)
message_area.grid(row=0)

# Add a "holder" for our buttons
button_holder = tk.Frame(game)
button_holder.grid(row=1)


#
# Function Definitions:
#


def check_victory(row, col):
    """
    Checks to see if there's a winner based on the last-played postion
    """
    # check if previous move caused a win on vertical line
    if board[0][col] == board[1][col] == board[2][col]:
        return True

    # check if previous move caused a win on horizontal line
    if board[row][0] == board[row][1] == board[row][2]:
        return True

    # check if previous move was on the main diagonal and caused a win
    if row == col and board[0][0] == board[1][1] == board[2][2]:
        return True

    # check if previous move was on the secondary diagonal and caused a win
    if row + col == 2 and board[0][2] == board[1][1] == board[2][0]:
        return True

    return False


def make_move(row, col):
    """
    Makes a move for the current player (whether human or computer).

    This function returns `True` if the move results in a win, otherwise it returns `False`.
    """
    # If the clicked space is not already occupied, set it to the current player
    if not board[row][col]:
        # Set "X" or "O" on our virtual game board
        board[row][col] = game.current_player

        # Change the button text to the current player (X or O)
        button = buttons[row][col]
        button.configure(text=game.current_player)

    # Check if there's a winner, otherwise, switch the current player
    if check_victory(row, col):
        messagebox.showinfo("Game over!", "The winner is: " + game.current_player + "!")
        new_game()
        return True

    else:
        if game.current_player == "X":
            game.current_player = "O"
        else:
            game.current_player = "X"

        message.set(game.current_player + "'s move")
        return False


def on_click(event):
    """
    This function gets called any time a button is clicked. Each button has a `row` and `col`
    attribute, which we can pass to the `make_move()` function.
    """
    clicked_button = event.widget
    row = clicked_button.row
    col = clicked_button.col

    # The `make_move()` function returns `True` if it results in a win.
    # We store that result in a variable so we can use it in the next step.
    is_winning_move = make_move(row, col)

    if not is_winning_move:
        # Wait 1 second before calling `ai_move()` to make it look like the computer is "thinking".
        game.after(1000, ai_move)


def new_game():
    """
    Clears the game board and starts a new game
    """
    # re-set the buttons list
    buttons.clear()

    # Remove existing button widgets
    for widget in button_holder.winfo_children():
        widget.destroy()

    # Fill the window with new buttons
    for row in range(0, 3):
        buttons.append([])
        for col in range(0, 3):
            button = make_button(row, col)
            buttons[row].append(button)
            board[row][col] = ""

    # X player goes first
    game.current_player = "X"
    message.set(game.current_player + "'s move")


def make_button(row, col):
    """
    Creates a button in the given space
    """
    button = tk.Button(button_holder, text=" ", font="Times 20 bold",
                       bg="gray", fg="white", height=4, width=8)
    button.row = row
    button.col = col
    button.grid(row=row, column=col, stick=tk.S + tk.N + tk.E + tk.W)

    # Call on_click() when the button is clicked
    button.bind("<Button-1>", on_click)

    # return the button object for use later on
    return button


def ai_move():
    """
    This function contains the AI for the computer to make a move on behalf of the current player.
    """
    # "DUMB" ALGORITHM:
    for row in range(0, 3):
        for col in range(0, 3):
            if board[row][col] == "":
                make_move(row, col)
                # by returning here, we stop the function entirely (no more looping)
                return


# The rest of the code is called immediately when the script starts:
new_game()

# Start the app
game.mainloop()