from dataclasses import dataclass, field
from abc import ABC, abstractmethod 
import random
import time

@dataclass
class User:
    name: str = "User"

    def putName(self):
        self.name = input("Please provide your name: ")
    
    def makeMove(self, board):
        piecesOnBoard = [piece for piece in board.pieces if (isinstance(piece, UserPiece) and bool(board.whichMovesPossible(piece)))]
        promptPiece = ""
        for i in range(len(piecesOnBoard)):
            promptPiece += str(i) + "-piece on " + piecesOnBoard[i].positionHorizontal + str(piecesOnBoard[i].positionVertical) + "  "

        while True:
            i = input("Which piece would you like to move? " + promptPiece)
            if i.isdigit() and int(i) in range(len(piecesOnBoard)):
                i = int(i)
                break
            else:
                print()
                print("Please answer with one of the given options (e.g. type '0').")
                print()

        chosenPiece = piecesOnBoard[i]

        promptMoves = ""
        possibleMoves = board.whichMovesPossible(chosenPiece)
        for i in range(len(possibleMoves)):
            promptMoves += str(i) + "-" + possibleMoves[i] + "  "
        
        while True:
            j = input("Where would you like to move it to? " + promptMoves)
            if j.isdigit() and int(j) in range(len(piecesOnBoard)):
                j = int(j)
                break
            else:
                print()
                print("Please answer with one of the given options (e.g. type '0').")
                print()

        chosenMove = possibleMoves[j]
        chosenPiece.move(chosenMove)

            # Check if the chosen piece matches any system piece and remove it
        for i in range(len(board.pieces)):
            if isinstance(board.pieces[i], SystemPiece) and chosenPiece == board.pieces[i]:
                board.removePiece(i)
                break


def createStartingBoard():
    return {"A": [" "]*3, "B": [" "]*3, "C": [" "]*3}


class Board:
    def __init__(self, pieces):
        self.const = createStartingBoard()
        self.pieces = pieces
    
    def removePiece(self, piece_num):
        self.pieces.pop(piece_num)

    def showBoard(self):
        self.const = createStartingBoard()
        for piece in self.pieces:
            self.const[piece.positionHorizontal][piece.positionVertical] = piece.col
        print("  | 0 | 1 | 2")
        print("---------------")
        for key, x in self.const.items():
            print(key,"|", x[0],"|",x[1],"|",x[2])
            print("---------------")
        print()

            

    def whichMovesPossible(self, piece):
        possibleMoves = []
        if not piece in self.pieces:
            print("This piece is not on the board.")
        if isinstance(piece, UserPiece):
            if piece.positionVertical>0 and \
                ((piece.positionHorizontal == "C" and self.const["B"][piece.positionVertical-1] == "x") or \
                (piece.positionHorizontal == "B" and self.const["A"][piece.positionVertical-1] == "x")):
                possibleMoves.append("left")
            if (piece.positionHorizontal == "C" and self.const["B"][piece.positionVertical] == " ") or \
                (piece.positionHorizontal == "B" and self.const["A"][piece.positionVertical] == " "):
                possibleMoves.append("straight")
            if piece.positionVertical<2 and \
                ((piece.positionHorizontal == "C" and self.const["B"][piece.positionVertical+1] == "x") or \
                (piece.positionHorizontal == "B" and self.const["A"][piece.positionVertical+1] == "x")):
                possibleMoves.append("right")
        if isinstance(piece, SystemPiece):
            if piece.positionVertical>0 and \
                ((piece.positionHorizontal == "A" and self.const["B"][piece.positionVertical-1] == "o") or \
                (piece.positionHorizontal == "B" and self.const["C"][piece.positionVertical-1] == "o")):
                possibleMoves.append("left")
            if (piece.positionHorizontal == "A" and self.const["B"][piece.positionVertical] == " ") or \
                (piece.positionHorizontal == "B" and self.const["C"][piece.positionVertical] == " "):
                possibleMoves.append("straight")
            if piece.positionVertical<2 and \
                ((piece.positionHorizontal == "A" and self.const["B"][piece.positionVertical+1] == "o") or \
                (piece.positionHorizontal == "B" and self.const["C"][piece.positionVertical+1] == "o")):
                possibleMoves.append("right")
        return possibleMoves
    

    def endGame(self):
        movesPossible = []
        userPieces = []
        systemPieces = []
        for piece in self.pieces:
            if isinstance(piece, UserPiece):
                userPieces.append(piece)

                if piece.positionHorizontal == "A":
                    print("Game Over")
                    return True
                
            elif isinstance(piece, SystemPiece):
                systemPieces.append(piece)
                if piece.positionHorizontal == "C":
                    print("Game Over")
                    return True
                
            movesPossible.append(bool(self.whichMovesPossible(piece)))

        if (not bool(userPieces)) or (not bool(systemPieces)):
            print("Game Over")
            return True
        
        if not any(movesPossible):
            print("Game Over")
            return True
        
        return False




class Piece(ABC):
    def __init__(self, positionHorizontal, positionVertical):
        super().__init__()
        self.positionHorizontal = positionHorizontal
        self.positionVertical = positionVertical
    
    def __eq__(self, value):
        if not isinstance(value, Piece):
            raise ValueError(f"{value} is not part of Piece class")
        return(self.positionHorizontal == value.positionHorizontal and
               self.positionVertical == value.positionVertical )

    def __str__(self):
        return f"{self.col} in {self.positionHorizontal}"+ str(self.positionVertical)

    def move(self):
        pass


class UserPiece(Piece):
    def __init__(self, positionHorizontal, positionVertical):
        super().__init__(positionHorizontal, positionVertical)
        self.col = "o"
    def move(self, direction):
        if self.positionHorizontal == "C":
            self.positionHorizontal = "B"
        elif self.positionHorizontal == "B":
            self.positionHorizontal = "A"
        if direction == "left":
            self.positionVertical -= 1
        if direction == "right":
            self.positionVertical += 1


class SystemPiece(Piece):
    def __init__(self,positionHorizontal, positionVertical):
        super().__init__(positionHorizontal, positionVertical)
        self.col = "x"
    def move(self, direction):
        if self.positionHorizontal == "A":
            self.positionHorizontal = "B"
        elif self.positionHorizontal == "B":
            self.positionHorizontal = "C"
        if direction == "left":
            self.positionVertical -= 1
        if direction == "right":
            self.positionVertical += 1

class Strategy:
    def __init__(self):
        self.consts = [
            {"A": ["x", "x", "x"], "B": ["o", " ", " "], "C": [" ", "o", "o"]},
            {"A": ["x", "x", "x"], "B": [" ", " ", "o"], "C": ["o", "o", " "]},
            {"A": ["x", "x", "x"], "B": [" ", "o", " "], "C": ["o", " ", "o"]},
            {"A": ["x", " ", "x"], "B": ["x", "o", " "], "C": [" ", " ", "o"]},
            {"A": ["x", " ", "x"], "B": [" ", "o", "x"], "C": ["o", " ", " "]},
            {"A": [" ", "x", "x"], "B": ["o", "x", " "], "C": [" ", " ", "o"]},
            {"A": ["x", "x", " "], "B": [" ", "x", "o"], "C": ["o", " ", " "]},
            
            {"A": ["x", " ", "x"], "B": ["o", "o", " "], "C": [" ", "o", " "]},
            {"A": ["x", " ", "x"], "B": [" ", "o", "o"], "C": [" ", "o", " "]},
            {"A": ["x", "x", " "], "B": ["o", " ", "o"], "C": [" ", " ", "o"]},
            {"A": [" ", "x", "x"], "B": ["o", " ", "o"], "C": ["o", " ", " "]},
            {"A": [" ", "x", "x"], "B": [" ", "x", "o"], "C": ["o", " ", " "]},
            {"A": ["x", "x", " "], "B": ["o", "x", " "], "C": [" ", " ", "o"]},
            {"A": [" ", "x", "x"], "B": ["x", "o", "o"], "C": ["o", " ", " "]},
            {"A": ["x", "x", " "], "B": ["o", "o", "x"], "C": [" ", " ", "o"]},

            {"A": ["x", " ", "x"], "B": ["x", " ", "o"], "C": [" ", "o", " "]},
            {"A": ["x", " ", "x"], "B": ["o", " ", "x"], "C": [" ", "o", " "]},
            {"A": [" ", "x", "x"], "B": [" ", "o", " "], "C": [" ", " ", "o"]},
            {"A": ["x", "x", " "], "B": [" ", "o", " "], "C": ["o", " ", " "]},
            {"A": [" ", "x", "x"], "B": [" ", "o", " "], "C": ["o", " ", " "]},
            {"A": ["x", "x", " "], "B": [" ", "o", " "], "C": [" ", " ", "o"]},

            {"A": ["x", " ", "x"], "B": ["o", " ", " "], "C": [" ", " ", "o"]},
            {"A": ["x", " ", "x"], "B": [" ", " ", "o"], "C": ["o", " ", " "]},
            {"A": [" ", " ", "x"], "B": ["x", "x", "o"], "C": [" ", " ", " "]},
            {"A": ["x", " ", " "], "B": ["o", "x", "x"], "C": [" ", " ", " "]},
            {"A": [" ", " ", "x"], "B": ["o", "o", "o"], "C": [" ", " ", " "]},
            {"A": ["x", " ", " "], "B": ["o", "o", "o"], "C": [" ", " ", " "]},
            {"A": [" ", "x", " "], "B": ["x", "o", "o"], "C": [" ", " ", " "]},
            {"A": [" ", "x", " "], "B": ["o", "o", "x"], "C": [" ", " ", " "]},

            {"A": ["x", " ", " "], "B": ["x", "x", "o"], "C": [" ", " ", " "]},
            {"A": [" ", " ", "x"], "B": ["o", "x", "x"], "C": [" ", " ", " "]},
            {"A": ["x", " ", " "], "B": [" ", "o", "x"], "C": [" ", " ", " "]},
            {"A": [" ", " ", "x"], "B": ["x", "o", " "], "C": [" ", " ", " "]},

            {"A": [" ", "x", " "], "B": [" ", "x", "o"], "C": [" ", " ", " "]},
            {"A": [" ", "x", " "], "B": ["x", "o", " "], "C": [" ", " ", " "]},
            {"A": ["x", " ", " "], "B": ["x", "o", " "], "C": [" ", " ", " "]},
            {"A": [" ", " ", "x"], "B": [" ", "o", "x"], "C": [" ", " ", " "]},
        ]

        self.allmoves = []

        for const in self.consts:
            const_options = []
            for column, row in const.items():
                for i in range(len(row)):
                    positionHorizontal = column
                    positionVertical = i
                    if row[i] == "x":
                        possiblePiece = [positionHorizontal, positionVertical]
                        possibleMoves = []
                        if positionVertical>0 and \
                            ((positionHorizontal == "A" and const["B"][positionVertical-1] == "o") or \
                            (positionHorizontal == "B" and const["C"][positionVertical-1] == "o")):
                            possibleMoves.append("left")
                        if (positionHorizontal == "A" and const["B"][positionVertical] == " ") or \
                            (positionHorizontal == "B" and const["C"][positionVertical] == " "):
                            possibleMoves.append("straight")
                        if positionVertical<2 and \
                            ((positionHorizontal == "A" and const["B"][positionVertical+1] == "o") or \
                            (positionHorizontal == "B" and const["C"][positionVertical+1] == "o")):
                            possibleMoves.append("right")
                        if bool(possibleMoves):
                            for move in possibleMoves:
                                const_options.append((positionHorizontal,positionVertical,move))
            self.allmoves.append([const, const_options])

    def recognizeConst(self,board):
        for i in range(len(self.consts)):
            if self.consts[i] == board.const:
                return(i)
            
    def pickRandomMove(self, i):
        randomOption = random.randrange(len(self.allmoves[i][1]))
        return randomOption
    
    def makeMove(self, board):
        whichConst = self.recognizeConst(board)
        whichMove = self.pickRandomMove(whichConst)
        selectedMove = self.allmoves[whichConst][1][whichMove]
        print("The system choses the piece on " + selectedMove[0] + str(selectedMove[1]) + " to go " + selectedMove[2] + ".")
        time.sleep(2)
        for piece in board.pieces:
            if piece.positionHorizontal == selectedMove[0] and \
            piece.positionVertical == selectedMove[1] and \
            isinstance(piece, SystemPiece):
                piece.move(selectedMove[2])
                for k in range(len(board.pieces)):
                    if isinstance(board.pieces[k], UserPiece) and \
                    board.pieces[k] == piece:
                        board.removePiece(k)
                        return [whichConst, whichMove]
                return [whichConst, whichMove]

    
    def removeLosingMove(self, whichConst, whichMove):
        self.allmoves[whichConst][1].pop(whichMove)


class Counter:
    def __init__(self):
        self.totGames = 0
        self.wonByUser = 0

    def displayWhoWon(self, winner, user):
        self.totGames += 1
        if winner == "u":
            self.wonByUser +=1
            print(user.name + " won!")
            print()
        else:
            print("The system won!")
            print()
    
    def displayCount(self, user):
        print("So far, " + user.name + " has won " + str(self.wonByUser) + " out of " + str(self.totGames) +" times.")