#include <stdio.h>
#include <string.h>

#include "raylib.h"
#include "raymath.h"

#define MAP_WIDTH_MAX 5
#define MAP_HEIGHT_MAX 5

#define MAX_ATOUT (4*4 - 3)
#define MAX_PLAYER 6
#define MAX_PIOCHE (4*13 + 2 - 6)
#define MAX_MAIN (MAX_PIOCHE - 3*5)

#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))

typedef enum CarteType {
    CARTE_COEUR,
    CARTE_TREFLE,
    CARTE_CARREAU,
    CARTE_PIQUE,
    CARTE_TYPE_COUNT,
} CarteType;

typedef enum CarteValue {
    CARTE_VALET = 11,
    CARTE_REINE,
    CARTE_ROI,
    CARTE_AS,
    CARTE_JOKER,
    CARTE_VALUE_COUNT,
} CarteValue;

typedef struct Carte {
    CarteValue value;
    CarteType type;
} Carte;

typedef struct Map {
    int width, height;
    int spawn_x, spawn_y;
    Carte cartes[MAP_WIDTH_MAX*MAP_HEIGHT_MAX];
} Map;

#define O (Carte) {.value = -1, .type = -1}
#define X (Carte) {.value = 2,  .type =  0}
Map default_map = {3, 3, 1, 1, {
    X, X, X,
    X, X, X,
    X, X, X,
}};
Map grande_map = {5, 5, 2, 2, {
    O, O, X, O, O,
    O, X, X, X, O,
    X, X, X, X, X,
    O, X, X, X, O,
    O, O, X, O, O,
}};
#undef O
#undef X

typedef struct Player {
    int x, y;
    Carte role;
    int nb_atout;
    Carte atouts[MAX_ATOUT];
    int nb_cartes;
    Carte cartes[MAX_MAIN];
} Player;

typedef struct Game {
    int nb_player;
    Player players[MAX_PLAYER];
    int pioche_len;
    Carte pioche[MAX_PIOCHE];
    Map map;
} Game;

// name = 7C, TK JK
// will write only 2 char in buff
void carte_name(Carte carte, char* buff)
{
    if (carte.value < 10 && carte.value > 1) {
        buff[0] = '0' + carte.value;
    } else {
        switch (carte.value) {
        case 10:          buff[0] = 'T'; break;
        case CARTE_VALET: buff[0] = 'V'; break;
        case CARTE_REINE: buff[0] = 'Q'; break;
        case CARTE_ROI:   buff[0] = 'K'; break;
        case CARTE_AS:    buff[0] = 'A'; break;
        case CARTE_JOKER: buff[0] = 'J'; break;
        default:          buff[0] = '?'; break;
        }
    }
    switch (carte.type) {
        case CARTE_COEUR:   buff[1] = 'C'; break;
        case CARTE_TREFLE:  buff[1] = 'T'; break;
        case CARTE_CARREAU: buff[1] = 'K'; break;
        case CARTE_PIQUE:   buff[1] = 'P'; break;
        default:            buff[1] = '?'; break;
    }
}

int valid_carte(Carte carte)
{
    return !(carte.value < 2 || carte.value > CARTE_JOKER ||
        carte.type < CARTE_COEUR || carte.type >= CARTE_TYPE_COUNT);
}

Vector2 window = {1920, 1080};

void game_init(Game* game, Map map, int nb_player)
{
    if (nb_player < 3) return;

    Carte roles[] = {
        {CARTE_REINE, CARTE_COEUR}, // dora, always in game
        {CARTE_VALET, CARTE_CARREAU}, // babouche
        {CARTE_REINE, CARTE_TREFLE}, // chipeur
        {CARTE_ROI, CARTE_PIQUE}, // grumpy
        {CARTE_VALET, CARTE_COEUR}, // malveillant
        {CARTE_ROI, CARTE_CARREAU}, // diego
    };
    int len = sizeof(roles)/sizeof(*roles);

    if (nb_player > len) return;

    // pioche

    int count = 0;
    for (CarteType type = 0; type < CARTE_TYPE_COUNT; type++) {
        for (CarteValue value = 2; value <= CARTE_AS; value++) {
            int i;
            for (i = 0; i < len; i++) {
                if (roles[i].value == value && roles[i].type == type) break;
            }
            if (i < len) continue;

            game->pioche[count] = (Carte) {value, type};
            count++;
        }
    }
    game->pioche[count++] = (Carte) {CARTE_JOKER, CARTE_COEUR};
    game->pioche[count++] = (Carte) {CARTE_JOKER, CARTE_PIQUE};
    game->pioche_len = count;

    for (int i = 0; i < game->pioche_len; i++) {
        int idx = GetRandomValue(0, game->pioche_len-1);
        Carte swap = game->pioche[i];
        game->pioche[i] = game->pioche[idx];
        game->pioche[idx] = swap;
    }

    // player

    int picked[sizeof(roles)/sizeof(*roles)] = {0};
    int nb_atout[MAX_PLAYER] = {0};

    int dora_idx = GetRandomValue(0, nb_player-1);
    game->players[dora_idx].role = roles[0];
    picked[0] = 1;
    game->players[dora_idx].x = map.spawn_x;
    game->players[dora_idx].y = map.spawn_y;

    for (int i = 0; i < nb_player; i++) {
        if (i == dora_idx) continue;

        int idx;
        do {
            idx = GetRandomValue(1, len-1);
        } while (picked[idx]);

        picked[idx] = 1;
        game->players[i].role = roles[idx];
        game->players[i].x = map.spawn_x;
        game->players[i].y = map.spawn_y;
    }

    for (int i = 0; i < nb_player; i++) {
        for (int c = 0; c < 5; c++) {
            game->players[i].cartes[c] = game->pioche[--game->pioche_len];
        }
        game->players[i].nb_cartes = 5;
    }

    game->nb_player = nb_player;

    // map

    for (int i = 0; i < map.width*map.height; i++) {
        if (!valid_carte(map.cartes[i])) {
            game->map.cartes[i] = (Carte) {-1, -1};
        } else {
            game->map.cartes[i] = game->pioche[--game->pioche_len];
        }
    }
    game->map.width = map.width;
    game->map.height = map.height;
    game->map.spawn_x = map.spawn_x;
    game->map.spawn_y = map.spawn_y;
}


void attack(Game* game, int player, int carte, int x, int y)
{
    if (x < 0 || x >= game->map.width || y < 0 || y >= game->map.height) return;

    Carte c = game->players[player].cartes[carte];

    switch (c.type) {
    case CARTE_PIQUE:
        // attaquer joueur
        break;
    case CARTE_TREFLE:
        // poser piege
        break;
    case CARTE_COEUR:
    case CARTE_CARREAU:
    default:
        return;
    }

    // Player p = game->players[player];
    Carte to = game->map.cartes[y*game->map.width + x];

    char buff[3];
    carte_name(to, buff);
    buff[2] = 0;
    printf("to %s\n", buff);

    (void) game;
}

int main()
{
    InitWindow(window.x, window.y, "DORA");
    SetWindowState(/*FLAG_FULLSCREEN_MODE|*/FLAG_WINDOW_RESIZABLE);
    SetTargetFPS(60);

    Game game = {0};
    game_init(&game, grande_map, 5);

    while (!WindowShouldClose()) {

        // update

        if (IsWindowResized()) {
            window.x = GetScreenWidth();
            window.y = GetScreenHeight();
        }

        static int player = 0;
        if (IsKeyPressed(KEY_SPACE))
            player = (player+1)%game.nb_player;
        if (IsKeyPressed(KEY_S))
            game.players[player].y++;
        if (IsKeyPressed(KEY_W))
            game.players[player].y--;
        if (IsKeyPressed(KEY_D))
            game.players[player].x++;
        if (IsKeyPressed(KEY_A))
            game.players[player].x--;

        static unsigned int carte = 4269+1;
        if (IsKeyPressed(KEY_E))
            carte++;
        if (IsKeyPressed(KEY_Q))
            carte--;

        // code skibidi degeu, parti draw() a refaire plus tard
        BeginDrawing();
        {
            ClearBackground(GRAY);

            Vector2 off = {0, 0};
            int size = 0;
            if (window.x < window.y) {
                size = window.x / game.map.width;
                off.y = (window.y - window.x) / 2;
            } else  {
                size = window.y / game.map.height;
                off.x = (window.x - window.y) / 2;
            }

            for (int y = 0; y < game.map.height; y++) {
                for (int i = 0; i < game.map.width; i++) {
                    Carte carte = game.map.cartes[y*game.map.width+i];
                    if (!valid_carte(carte))
                        continue;

                    Rectangle tile = {off.x+size*i, off.y+size*y, size, size};
                    DrawRectangleRec(tile, BLACK);

                    char buff[3];
                    carte_name(carte, buff);
                    buff[2] = 0;
                    DrawText(buff, tile.x, tile.y, size/5, ORANGE);
                }
            }

            Vector2 mouse = GetMousePosition();
            Vector2 hover = {mouse.x * game.map.height / window.y, mouse.y * game.map.height / window.y};
            hover.x -= 2;
            Rectangle tile = {off.x+size*(int)hover.x, off.y+size*(int)hover.y, size, size};
            DrawRectangleRec(tile, IsMouseButtonDown(MOUSE_BUTTON_LEFT) ? RED : WHITE);

            if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
                attack(&game, player, carte%game.players[player].nb_cartes, hover.x, hover.y);

            for (int i = 0; i < game.nb_player; i++) {
                char buff[3];
                carte_name(game.players[i].role, buff);
                buff[2] = 0;
                Rectangle tile = {off.x+size*(game.players[i].x), off.y+size*(game.players[i].y), size, size};
                DrawText(buff, tile.x+size-MeasureText(buff, size/5)-5, tile.y+size-size/5, 
                    i == player
                    ? size/4
                    : size/5,
                    i == player
                    ? WHITE
                    : ColorFromHSV(((float)i/MAX_PLAYER)*360, 1, 1)
                );
            }

            for (int i = 0; i < game.players[player].nb_cartes; i++) {
                char buff[3];
                carte_name(game.players[player].cartes[i], buff);
                buff[2] = 0;
                DrawText(buff, 0, size/5*i, size/5, i == (int)carte%game.players[player].nb_cartes ? WHITE : RED);
            }
        }
        EndDrawing();
    }
}
