#include "morpion.h"

bool playerTurn;
int aiLevel;

int main()
{
    gameLoop();
    return 0;
}

void gameLoop()
{
    while (1)
    {
        setPlayerTurn(true);
        int inputLevel = -1;
        while (inputLevel < 1 || inputLevel > 3)
        {
            printf("> Wich level of difficulty do you want ? (1 : Easy, 2: Medium, 3: Impossible)\n");
            scanf("%d", &inputLevel);
            setAiLevel(inputLevel);
        }

        char *grid = newGame();
        srand(time(NULL));
        while (!won(grid))
        {
            if (isDraw(grid))
            {
                break;
            }
            if (isPlayerTurn())
            {
                char input;
                if (input != '\n' && input != ' ')
                {
                    printGrid(grid);
                    printf("> Waiting input [type h for help] \n");
                }
                scanf("%c", &input);
                if (input == 'h')
                {
                    printf(
                        "\nCommand list:\n"
                        " h = help\n"
                        " c <x> <y> = place a cross at coordinates (x = column, y = row)\n"
                        " q = quit\n");
                }
                else if (input == 'q')
                {
                    printf("Really ? You're giving up ?\nWell (╯°□°)╯︵ ┻━┻ shame on you.\n");
                    deleteGame(grid);
                    exit(0);
                }
                else if (input == 'c')
                {
                    int x, y;
                    scanf("%d %d", &x, &y);
                    if (play(x, y, grid))
                    {
                        changePlayerTurn();
                    }
                }
            }
            else
            {
                if (getAiLevel() == 1)
                {
                    aiLevel1(grid);
                }
                else if (getAiLevel() == 2)
                {
                    aiLevel2(grid);
                }
                else
                {
                    aiLevel3(grid);
                }
                changePlayerTurn();
            }
        }
        printGrid(grid);
        if (checkWin('X', grid))
        {
            printf("Congratulation you won !\n");
        }
        else if (checkWin('O', grid))
        {
            printf("Too bad you lost.\n");
        }
        else if (isDraw(grid))
        {
            printf("It's a draw\n");
        }
        deleteGame(grid);

        char inputPlayAgain;
        printf("> Wanna play again ? (y/n) \n");
        scanf(" %c", &inputPlayAgain);
        if (inputPlayAgain != 'y' && inputPlayAgain != 'Y')
        {
            break;
        }
    }
}

char *newGame()
{
    char *grid = malloc(9 * sizeof(char));
    memset(grid, '.', 9);

    return grid;
}

void deleteGame(char *grid)
{
    if (grid)
    {
        free(grid);
    }
}

bool play(int x, int y, char *grid)
{
    if (inGrid(x, y))
    {
        if (grid[y * 3 + x] != '.')
        {
            if (isPlayerTurn())
            {
                printf("Invalid Move this case is already occupied\n");
            }
            return false;
        }
        else
        {
            if (isPlayerTurn())
            {
                grid[y * 3 + x] = 'X';
            }
            else
            {
                grid[y * 3 + x] = 'O';
            }
        }
    }
    else
    {
        if (isPlayerTurn())
        {
            printf("Invalid Move the coordinate given are outside the game grid\n");
        }
        return false;
    }
    return true;
}

void aiLevel1(char *grid)
{
    if (!isPlayerTurn())
    {
        int x = rand() % 3;
        int y = rand() % 3;
        while (!play(x, y, grid))
        {
            x = rand() % 3;
            y = rand() % 3;
        }
    }
}

void aiLevel2(char *grid)
{
for (int x = 0; x < 3; x++)
    {
        for (int y = 0; y < 3; y++)
        {
            if (grid[y * 3 + x] == '.')
            {
                grid[y * 3 + x] = 'O';
                if(evaluate(grid) == 10){
                    return;
                }
                grid[y * 3 + x] = '.';


            }
        }
    }
for (int x = 0; x < 3; x++)
    {
        for (int y = 0; y < 3; y++)
        {
            if (grid[y * 3 + x] == '.')
            {
                grid[y * 3 + x] = 'X';
                if(evaluate(grid) == -10){
                    grid[y * 3 + x] = 'O';
                    return;
                }
                grid[y * 3 + x] = '.';


            }
        }
    }
    aiLevel1(grid);
}

void aiLevel3(char *grid)
{
    int bestVal = -1000;
    int bestRow = -1;
    int bestCol = -1;

    for (int x = 0; x < 3; x++)
    {
        for (int y = 0; y < 3; y++)
        {
            if (grid[y * 3 + x] == '.')
            {
                grid[y * 3 + x] = 'O';
                int moveVal = minimax(grid, 0, 0);
                grid[y * 3 + x] = '.';

                if (moveVal > bestVal)
                {
                    bestRow = y;
                    bestCol = x;
                    bestVal = moveVal;
                }
            }
        }
    }
    play(bestCol, bestRow, grid);
}

int minimax(char *grid, int depth, int aiTurn)
{
    int score = evaluate(grid);

    if (score == 10)
        return score - depth; // plus on gagne vite mieux c'est donc le meilleur score doit avoir le plus petit depth
    if (score == -10)
        return score + depth; // inversement plus on met de temps a perdre mieux c'est donc la defaite avec le grand depth et mieux

    if (isDraw(grid))
        return 0; // egalité c'est passable mieux que perdre pire que gagner

    if (aiTurn)
    {
        int best = -1000;

        for (int x = 0; x < 3; x++)
        {
            for (int y = 0; y < 3; y++)
            {
                if (grid[y * 3 + x] == '.')
                {
                    grid[y * 3 + x] = 'O';
                    int val = minimax(grid, depth + 1, 0);
                    if (val > best)
                    {
                        best = val;
                    }
                    grid[y * 3 + x] = '.';
                }
            }
        }
        return best;
    }

    else
    {
        int best = 1000;

        for (int x = 0; x < 3; x++)
        {
            for (int y = 0; y < 3; y++)
            {
                if (grid[y * 3 + x] == '.')
                {
                    grid[y * 3 + x] = 'X';
                    int val = minimax(grid, depth + 1, 1);
                    if (val < best)
                    {
                        best = val;
                    }
                    grid[y * 3 + x] = '.';
                }
            }
        }
        return best;
    }
}

int evaluate(char *grid)
{
    if (checkWin('X', grid))
    {
        return -10;
    }
    else if (checkWin('O', grid))
    {
        return 10;
    }
    else
    {
        return 0;
    }
}

bool won(char *grid)
{
    return (checkWin('X', grid) || checkWin('O', grid));
}

bool isDraw(char *grid)
{
    for (int x = 0; x < 3; x++)
    {
        for (int y = 0; y < 3; y++)
        {
            if (grid[y * 3 + x] == '.')
            {
                return false;
            }
        }
    }
    return true;
}

bool checkWin(char player, char *grid)
{
    // Vérifie les lignes
    for (int row = 0; row < 3; row++)
    {
        if (grid[row * 3] == player && grid[row * 3 + 1] == player && grid[row * 3 + 2] == player)
        {
            return true;
        }
    }

    // Vérifie les colonnes
    for (int col = 0; col < 3; col++)
    {
        if (grid[col] == player && grid[col + 3] == player && grid[col + 6] == player)
        {
            return true;
        }
    }

    // Vérifie diagonale principale
    if (grid[0] == player && grid[4] == player && grid[8] == player)
    {
        return true;
    }

    // Vérifie diagonale secondaire
    if (grid[2] == player && grid[4] == player && grid[6] == player)
    {
        return true;
    }

    return false;
}

void printGrid(char *grid)
{

    printf("+---+---+---+\n");
    for (int i = 0; i < 3; i++)
    {
        printf("|");
        for (int j = 0; j < 3; j++)
        {
            printf(" %c |", grid[i * 3 + j]);
        }
        printf("\n+---+---+---+\n");
    }
    printf("\n");
}

bool isPlayerTurn()
{
    return playerTurn;
}

void changePlayerTurn()
{
    playerTurn = !playerTurn;
}

bool inGrid(int x, int y)
{
    return (x >= 0 && x < 3 && y >= 0 && y < 3);
}

int getAiLevel()
{
    return aiLevel;
}

void setAiLevel(int Level)
{
    aiLevel = Level;
}

void setPlayerTurn(bool Turn)
{
    playerTurn = Turn;
}

int checkState(int x, int y, char* grid){
    if(grid[y * 3 + x] == 'X'){
        return 1;
    }
    else if(grid[y * 3 + x] == 'O'){
        return 2;
    }
        return 0;
}