import pygame
from copy import deepcopy
from .constants import BLACK, ROWS, GREEN, SQUARE_SIZE, COLS, WHITE
from .piece import Piece


class Board:
    def __init__(self):
        self.board = []
        self.greenLeft = self.whiteLeft = 12
        self.greenKings = self.whiteKings = 0
        self.green = (144, 184, 59)
        self._createBoard()

    def _drawSquares(self, win):
        win.fill(BLACK)
        for row in range(ROWS):
            for col in range(row % 2, ROWS, 2):
                pygame.draw.rect(win, self.green, (row * SQUARE_SIZE, col * SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE))

    def _createBoard(self):
        for row in range(ROWS):
            self.board.append([])
            for col in range(COLS):
                if col % 2 == ((row + 1) % 2):
                    if row < 3:
                        self.board[row].append(Piece(row, col, WHITE))
                        continue

                    if row > 4:
                        self.board[row].append(Piece(row, col, GREEN))
                        continue

                    self.board[row].append(0)
                    continue

                self.board[row].append(0)

    def draw(self, win):
        self._drawSquares(win)
        for row in range(ROWS):
            for col in range(COLS):
                piece = self.board[row][col]
                if piece != 0:
                    piece.draw(win)

    def move(self, piece, row, col):
        self.board[piece.row][piece.col], self.board[row][col] = self.board[row][col], self.board[piece.row][piece.col]
        piece.move(row, col)

        if row == ROWS - 1 or row == 0:
            piece.makeKing()

            if piece.colour == WHITE:
                self.whiteKings += 1

            if piece.colour == GREEN:
                self.greenKings += 1

    def remove(self, skipped):
        for piece in skipped:
            self.board[piece.row][piece.col] = 0
            if piece != 0:
                if piece.colour == GREEN:
                    self.greenLeft -= 1
                    continue
                self.whiteLeft -= 1

    def getAllMoves(self, colour):
        moves = []
        possibleMoves = []
        possiblePieces = []
        pieces = self.getAllPieces(colour)
        hasForcedCapture = False

        for piece in pieces:
            validMoves = self.getValidMoves(piece)

            # Check if there are forced capture moves for this piece
            forcedCaptureMoves = [move for move, skip in validMoves.items() if skip]
            if forcedCaptureMoves:
                hasForcedCapture = True
                possiblePieces.append(piece)
                possibleMoves.append({move: skip for move, skip in validMoves.items() if skip})

        if hasForcedCapture:
            # If there are forced capture moves, consider only those
            for i in range(len(possibleMoves)):
                for move, skip in possibleMoves[i].items():
                    tempBoard = deepcopy(self)
                    tempPiece = tempBoard.getPiece(possiblePieces[i].row, possiblePieces[i].col)
                    newBoard = self._simulateMove(tempPiece, move, tempBoard, skip)
                    moves.append(newBoard)
        else:
            # If no forced capture moves, consider all valid moves
            for piece in pieces:
                validMoves = self.getValidMoves(piece)
                for move, skip in validMoves.items():
                    tempBoard = deepcopy(self)
                    tempPiece = tempBoard.getPiece(piece.row, piece.col)
                    newBoard = self._simulateMove(tempPiece, move, tempBoard, skip)
                    moves.append(newBoard)

        return moves

    def _simulateMove(self, piece, move, board, skip):
        board.move(piece, move[0], move[1])
        if skip:
            board.remove(skip)

        return board

    def getPiece(self, row, col):
        return self.board[row][col]

    def winner(self):
        if self.greenLeft <= 0:
            return WHITE

        if self.whiteLeft <= 0:
            return GREEN

        return None

    def getValidMoves(self, piece):
        moves = {}
        forcedCapture = {}
        left = piece.col - 1
        right = piece.col + 1
        row = piece.row
        if piece.colour == GREEN or piece.king:
            moves.update(self._traverseLeft(row - 1, max(row - 3, -1), -1, piece.colour, left))
            moves.update(self._traverseRight(row - 1, max(row - 3, -1), -1, piece.colour, right))
        if piece.colour == WHITE or piece.king:
            moves.update(self._traverseLeft(row + 1, min(row + 3, ROWS), 1, piece.colour, left))
            moves.update(self._traverseRight(row + 1, min(row + 3, ROWS), 1, piece.colour, right))

        if len(moves.values()) <= 1:
            return moves

        movesValues = list(moves.values())
        movesKeys = list(moves.keys())

        forced = {}

        for i in range(len(movesKeys)):
            if not movesValues[i]:
                forced[movesKeys[i]] = moves[movesKeys[i]]
        if len(forced) != len(moves):
            forced.clear()
            for i in range(len(movesKeys)):
                if movesValues[i]:
                    forced[movesKeys[i]] = moves[movesKeys[i]]
            if len(forced) != len(moves):
                for i in range(len(movesKeys)):
                    if movesValues[i]:
                        forcedCapture[movesKeys[i]] = moves[movesKeys[i]]
            else:
                forcedCapture = forced
        else:
            forcedCapture = forced

        return forcedCapture

    def scoreOfTheBoard(self):
        return self.whiteLeft - self.greenLeft

    def getAllPieces(self, colour):
        pieces = []
        for row in self.board:
            for piece in row:
                if piece != 0 and piece.colour == colour:
                    pieces.append(piece)
        return pieces

    def _traverseLeft(self, start, stop, step, colour, left, skipped=[]):
        moves = {}
        last = []
        for row in range(start, stop, step):
            if left < 0:
                break
            mvs = self._traverse(row, left, skipped, moves, step, last, colour)
            if mvs is None:
                break
            elif isinstance(mvs, list):
                last = mvs
            else:
                moves.update(mvs)
            left -= 1
        return moves

    def _traverseRight(self, start, stop, step, colour, right, skipped=[]):
        moves = {}
        last = []
        for row in range(start, stop, step):
            if right >= COLS:
                break

            mvs = self._traverse(row, right, skipped, moves, step, last, colour)
            if mvs is None:
                break
            elif isinstance(mvs, list):
                last = mvs
            else:
                moves.update(mvs)

            right += 1
        return moves

    def _traverse(self, row, col, skipped, moves, step, last, colour):
        current = self.board[row][col]
        if current == 0:
            if skipped and not last:
                return None
            elif skipped:
                moves[(row, col)] = last + skipped
            else:
                moves[(row, col)] = last

            if last:
                if step == -1:
                    rowCalc = max(row - 3, 0)
                else:
                    rowCalc = min(row + 3, ROWS)
                moves.update(self._traverseLeft(row + step, rowCalc, step, colour, col - 1, skipped=last))
                moves.update(self._traverseRight(row + step, rowCalc, step, colour, col + 1, skipped=last))
            return None
        elif current.colour == colour:
            return None
        else:
            last = [current]
            return last

    def step(self, move, colour):
        start, end = self._decode(move)
        start[0] = start[0] - 1
        start[1] = start[1] - 1
        end[0] = end[0] - 1
        end[1] = end[1] - 1
        reward = 0
        done = False
        piece = self.getPiece(start[0], start[1])
        if piece == 0:
            newStart = end
            end = start
            start = newStart
            piece = self.getPiece(start[0], start[1])
        moves = self.getValidMoves(piece)
        for move, skip in moves.items():
            if tuple(end) == move:
                self._simulateMove(piece, move, self, skip)
                if len(skip) == 1:
                    reward = 2
                    break
                if len(skip) > 1:
                    reward = 3 + len(skip) * 0.2
                    break
                reward = -0.5
                break

        if self.winner() == colour:
            done = True
            reward = 10
        return reward, self, done


    def _decode(self, move):
        # Split digits back out
        str_code = str(move)
        # print(str_code)
        start_row = int(str_code[0])
        start_col = int(str_code[1])
        end_row = int(str_code[2])
        end_col = int(str_code[3])
        # Reconstruct positions
        start = [start_row, start_col]
        end = [end_row, end_col]
        return start, end

    # def reset(self):
    #     self.board = []
    #     self.whiteLeft = self.greenLeft = 12
    #     self.whiteKings = self.greenKings = 0
    #     self._createBoard()
    #     return self.board
