import sys

import pygame
import numpy as np
from matplotlib import pyplot as plt

from reinforcementLearning.ReinforcementLearning import ReinforcementLearning
from utilities.constants import WIDTH, HEIGHT, SQUARE_SIZE, WHITE, GREEN
from utilities.gameManager import GameManager
from minimax.minimaxAlgo import MiniMax

FPS = 60
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Draughts")


def getRowColFromMouse(pos):
    x, y = pos
    row = y // SQUARE_SIZE
    col = x // SQUARE_SIZE
    return row, col


def drawText(text, font, color, surface, x, y):
    textobj = font.render(text, 1, color)
    textrect = textobj.get_rect()
    textrect.topleft = (x, y)
    surface.blit(textobj, textrect)


def drawMultiLineText(surface, text, pos, font, color=pygame.Color('black')):
    words = [word.split(' ') for word in text.splitlines()]  # 2D array where each row is a list of words.
    space = font.size(' ')[0]  # The width of a space.
    max_width, max_height = surface.get_size()
    x, y = pos
    word_height = None
    for line in words:
        for word in line:
            word_surface = font.render(word, 0, color)
            word_width, word_height = word_surface.get_size()
            if x + word_width >= max_width:
                x = pos[0]  # Reset the x.
                y += word_height  # Start on new row.
            surface.blit(word_surface, (x, y))
            x += word_width + space
        x = pos[0]  # Reset the x.
        y += word_height  # Start on new row.


def main(difficulty=0):
    pygame.init()
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    menuClock = pygame.time.Clock()
    click = False
    width = screen.get_width()
    font = pygame.font.SysFont("", 25)

    if difficulty == 0:
        while True:
            # menu
            screen.fill((128, 128, 128))
            drawText('Main Menu', font, (255, 255, 255), screen, width / 2, 20)

            mx, my = pygame.mouse.get_pos()

            easy = pygame.Rect(width / 2 - 50, 100, 200, 50)
            pygame.draw.rect(screen, (0, 255, 0), easy)
            drawText("easy", font, (255, 255, 255), screen, width / 2, 100)
            medium = pygame.Rect(width / 2 - 50, 200, 200, 50)
            pygame.draw.rect(screen, (255, 125, 0), medium)
            drawText("medium", font, (255, 255, 255), screen, width / 2, 200)
            hard = pygame.Rect(width / 2 - 50, 300, 200, 50)
            pygame.draw.rect(screen, (255, 0, 0), hard)
            drawText("hard", font, (255, 255, 255), screen, width / 2, 300)
            rules = pygame.Rect(width / 2 - 50, 400, 200, 50)
            pygame.draw.rect(screen, (0, 0, 255), rules)
            drawText("rules", font, (255, 255, 255), screen, width / 2, 400)
            quitGame = pygame.Rect(width / 2 - 50, 500, 200, 50)
            pygame.draw.rect(screen, (0, 0, 0), quitGame)
            drawText("quit", font, (255, 255, 255), screen, width / 2, 500)

            if easy.collidepoint((mx, my)):
                if click:
                    difficulty = 1
                    break
            if medium.collidepoint((mx, my)):
                if click:
                    difficulty = 3
                    break
            if hard.collidepoint((mx, my)):
                if click:
                    difficulty = 5
                    break
            if rules.collidepoint((mx, my)):
                if click:
                    rulesGUI()
                    break
            if quitGame.collidepoint((mx, my)):
                if click:
                    pygame.quit()
                    sys.exit()
            click = False
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
                if event.type == pygame.MOUSEBUTTONDOWN:
                    if event.button == 1:
                        click = True

            pygame.display.update()
            menuClock.tick(60)

    game(difficulty)


def rulesGUI():
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    menuClock = pygame.time.Clock()
    click = False
    width = screen.get_width()
    titleFont = pygame.font.SysFont("", 48)
    font = pygame.font.SysFont("", 21)
    while True:
        screen.fill((128, 128, 128))
        drawText("Rules", titleFont, (255, 255, 255), screen, width / 2, 20)

        mx, my = pygame.mouse.get_pos()
        drawMultiLineText(screen, """Both the player and AI start with 12 pieces on the dark squares of the three rows closest to that 
        player's side. The row closest to each player is called the kings row or crownhead. The player moves first. 
        Then turns alternate. 
\n
Move rules
\n
There are two different ways to move in  utilities:
\n
Simple move: A simple move consists of moving a piece one square diagonally to an adjacent unoccupied dark square. 
Uncrowned pieces can move diagonally forward only; kings can move in any diagonal direction. Jump: A jump consists of 
moving a piece that is diagonally adjacent an opponent's piece, to an empty square immediately beyond it in the same 
direction (thus "jumping over" the opponent's piece front and back ). Pieces can jump diagonally forward only; kings 
can jump in any diagonal direction. A jumped piece is considered "captured" and removed from the game. Any piece, 
king or piece, can jump a king.
\n
Forced capture, is always mandatory: if a player has the option to jump, he/she must take it, even if doing so 
results in disadvantage for the jumping player. For example, a piecedated single jump might set up the player such 
that the opponent has a multi-jump in reply.
\n
Multiple jumps are possible, if after one jump, another piece is immediately eligible to be jumped by the moved 
piece—even if that jump is in a different diagonal direction. If more than one multi-jump is available, the player 
can choose which piece to jump with, and which sequence of jumps to make. The sequence chosen is not required to be 
the one that maximizes the number of jumps in the turn; however, a player must make all available jumps in the 
sequence chosen. Kings If a piece moves into the kings row on the opponent's side of the board, it is crowned as a 
king and gains the ability to move both forward and backward. If a piece moves into the kings row or if it jumps into 
the kings row, the current move terminates; the piece is crowned as a king but cannot jump back out as in a 
multi-jump until the next move.""", (50, 50), font)
        back = pygame.Rect(width / 2 - 50, 700, 200, 50)
        pygame.draw.rect(screen, (0, 0, 0), back)
        drawText("back", font, (255, 255, 255), screen, width / 2, 700)

        if back.collidepoint((mx, my)):
            if click:
                main()
                break

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:
                    click = True

        pygame.display.update()
        menuClock.tick(60)


def game(difficulty):
    run = True
    clock = pygame.time.Clock()
    gameManager = GameManager(WIN, GREEN)
    rl = ReinforcementLearning(gameManager.board.getAllMoves(WHITE), gameManager.board, WHITE, gameManager)
    model = rl.buildMainModel()
    model.load_weights("./modelWeights/model_final.h5")
    mm = MiniMax()
    totalReward = []
    winners = []
    for i in range(100):
        score = 0
        for j in range(200):
            print(j)
            clock.tick(FPS)
            reward = 0
            if gameManager.turn == WHITE:
                # mm = MiniMax()
                # value, newBoard = mm.AI(difficulty, WHITE, gameManager)
                # gameManager.aiMove(newBoard)
                # reward, newBoard = rl.AI(gameManager.board)
                actionSpace = rl.encodeMoves(WHITE, gameManager.board)
                if len(actionSpace) == 0:
                    print("Cannot make move")
                    continue
                totalMoves = len(actionSpace)
                # moves = np.squeeze(moves)
                moves = np.pad(actionSpace, (0, rl.maxSize - totalMoves), 'constant', constant_values=(1, 1))
                act_values = model.predict(rl.normalise(moves))
                val = np.argmax(act_values[0])
                val = val if val < totalMoves else totalMoves - 1
                reward, newBoard, done = gameManager.board.step(actionSpace[val], WHITE)

                # if newBoard is None:
                #     print("Cannot make move")
                #     continue
                gameManager.aiMove(newBoard)

            gameManager.update()
            pygame.display.update()

            if gameManager.turn == GREEN:
                value, newBoard = mm.AI(difficulty, GREEN, gameManager)
                gameManager.aiMove(newBoard)

            score += reward

            if gameManager.winner() is not None:
                print("Green" if gameManager.winner() == GREEN else "White", " wins")
                with open("winners.txt", "a+") as f:
                    f.write(str(gameManager.winner()) + "\n")
                winners.append(gameManager.winner())
                break

            # for event in pygame.event.get():
            #     if event.type == pygame.QUIT:
            #         break
            #     if event.type == pygame.MOUSEBUTTONDOWN:
            #         pos = pygame.mouse.get_pos()
            #         row, col = getRowColFromMouse(pos)
            #         # if gameManager.turn == GREEN:
            #         gameManager.select(row, col)

            gameManager.update()
            pygame.display.update()

        if gameManager.winner() is None:
            with open("winners.txt", "a+") as f:
                f.write(str(0) + "\n")
            winners.append(0)
        gameManager.reset()
        rl.resetScore()
        print("Game: ", i, " Reward: ", score)
        with open("rewards.txt", "a+") as f:
            f.write(str(score) + "\n")

        totalReward.append(score)
        # save model weights every 25 games
        if i % 250 == 0 and i != 0:
            rl.model.save("./modelWeights/model_" + str(i) + ".h5")
    # pygame.quit()

    rl.model.save("./modelWeights/model_final.h5")

    plt.plot([i for i in range(len(totalReward))], totalReward)
    plt.xlabel("Games")
    plt.ylabel("Reward")
    plt.show()

    fig, ax = plt.subplots()
    bar = ax.bar(["Draw", "White", "Green"], [winners.count(0), winners.count(WHITE), winners.count(GREEN)])
    ax.set(xlabel='Winner', ylabel='Frequency', ylim=[0, 500])
    ax.set_title("Winners")
    ax.bar_label(bar)
    plt.show()


main(3)
