#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
#include "maze.h"

#define WIN_W 1200
#define WIN_H 750
#define CELL 12
#define WALL_THICK 1
#define HUD_H 44

static TTF_Font *font_sm = NULL;
static TTF_Font *font_lg = NULL;
static TTF_Font *font_title = NULL;

static const Uint32 COL_BG      = 0xFF0d1117;
static const Uint32 COL_WALL    = 0xFF58a6ff;
static const Uint32 COL_CELL    = 0xFF0d1117;

static const Uint32 COL_BTN     = 0xFF238636;
static const Uint32 COL_BTN_H   = 0xFF2ea043;
static const Uint32 COL_BTN2    = 0xFF8957e5;
static const Uint32 COL_BTN2_H  = 0xFFa371f7;
static const Uint32 COL_BTN3    = 0xFFda3633;
static const Uint32 COL_BTN3_H  = 0xFFf85149;
static const Uint32 COL_TEXT    = 0xFFc9d1d9;
static const Uint32 COL_DIM     = 0xFF484f58;

static void draw_rect(SDL_Renderer *ren, int x, int y, int w, int h, Uint32 color) {
    Uint8 r = (color >> 16) & 0xFF, g = (color >> 8) & 0xFF, b = color & 0xFF, a = (color >> 24);
    SDL_SetRenderDrawColor(ren, r, g, b, a);
    SDL_Rect rect = {x, y, w, h};
    SDL_RenderFillRect(ren, &rect);
}

static void draw_text(SDL_Renderer *ren, int x, int y, const char *text, Uint32 color, TTF_Font *f) {
    if (!text || !text[0] || !f) return;
    Uint8 r = (color >> 16) & 0xFF, g = (color >> 8) & 0xFF, b = color & 0xFF;
    SDL_Color col = {r, g, b, 255};
    SDL_Surface *surf = TTF_RenderUTF8_Blended(f, text, col);
    if (!surf) return;
    SDL_Texture *tex = SDL_CreateTextureFromSurface(ren, surf);
    SDL_Rect dst = {x, y, surf->w, surf->h};
    SDL_RenderCopy(ren, tex, NULL, &dst);
    SDL_DestroyTexture(tex);
    SDL_FreeSurface(surf);
}

static int text_width(TTF_Font *f, const char *text) {
    int w, h;
    TTF_SizeUTF8(f, text, &w, &h);
    return w;
}

typedef struct {
    int x, y, w, h;
    Uint32 col, col_h;
    const char *label;
    TTF_Font *font;
} Button;

static int btn_hit(Button *b, int mx, int my) {
    return mx >= b->x && mx < b->x + b->w && my >= b->y && my < b->y + b->h;
}

static void btn_draw(SDL_Renderer *ren, Button *b, int hover) {
    Uint32 col = hover ? b->col_h : b->col;
    draw_rect(ren, b->x, b->y, b->w, b->h, col);
    int tw = text_width(b->font, b->label);
    int th;
    TTF_SizeUTF8(b->font, b->label, NULL, &th);
    draw_text(ren, b->x + (b->w - tw) / 2, b->y + (b->h - th) / 2, b->label, COL_TEXT, b->font);
}

static void draw_maze(SDL_Renderer *ren, Maze *m, int ox, int oy) {
    for (int r = 0; r < m->rows; r++)
        for (int c = 0; c < m->cols; c++)
            draw_rect(ren, ox + c * CELL, oy + r * CELL, CELL, CELL, COL_CELL);

    for (int r = 0; r < m->rows; r++)
        for (int c = 0; c < m->cols; c++) {
            int x = ox + c * CELL, y = oy + r * CELL;
            unsigned char w = m->cells[r][c];
            if (w & W_N) draw_rect(ren, x, y, CELL, WALL_THICK, COL_WALL);
            if (w & W_S) draw_rect(ren, x, y + CELL - WALL_THICK, CELL, WALL_THICK, COL_WALL);
            if (w & W_W) draw_rect(ren, x, y, WALL_THICK, CELL, COL_WALL);
            if (w & W_E) draw_rect(ren, x + CELL - WALL_THICK, y, WALL_THICK, CELL, COL_WALL);
        }
}

int main(int argc, char *argv[]) {
    (void)argc; (void)argv;
    srand((unsigned)time(NULL));
    SDL_Init(SDL_INIT_VIDEO);
    TTF_Init();

    SDL_Window *win = SDL_CreateWindow("Complexx Maze Generator",
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        WIN_W, WIN_H, 0);
    SDL_Renderer *ren = SDL_CreateRenderer(win, -1,
        SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);

    font_sm = TTF_OpenFont("/usr/share/fonts/TTF/DejaVuSansMono.ttf", 13);
    font_lg = TTF_OpenFont("/usr/share/fonts/TTF/DejaVuSansMono.ttf", 16);
    font_title = TTF_OpenFont("/usr/share/fonts/TTF/DejaVuSansMono.ttf", 18);
    if (!font_sm) font_sm = TTF_OpenFont("/usr/share/fonts/TTF/JetBrainsMonoNerdFont-Regular.ttf", 13);
    if (!font_lg) font_lg = TTF_OpenFont("/usr/share/fonts/TTF/JetBrainsMonoNerdFont-Regular.ttf", 16);
    if (!font_title) font_title = TTF_OpenFont("/usr/share/fonts/TTF/JetBrainsMonoNerdFont-Regular.ttf", 18);
#ifdef _WIN32
    if (!font_sm) font_sm = TTF_OpenFont("C:/Windows/Fonts/consola.ttf", 13);
    if (!font_lg) font_lg = TTF_OpenFont("C:/Windows/Fonts/consola.ttf", 16);
    if (!font_title) font_title = TTF_OpenFont("C:/Windows/Fonts/consola.ttf", 18);
#endif

    Maze maze;
    maze_init(&maze, 50, 80);

    MazeAlgo current_algo = ALG_RECURSIVE;
    const char *algo_names[] = {"RECURSIVE", "PRIM", "KRUSKAL", "ALDOUS-BRODER"};

    int maze_ox = 10, maze_oy = HUD_H + 10;

    /* buttons */
    int bx = 10, by = 10;
    Button btn_gen   = {bx, by, 110, 28, COL_BTN, COL_BTN_H, "GENERATE [G]", font_lg};
    Button btn_algo  = {bx + 120, by, 140, 28, COL_BTN2, COL_BTN2_H, algo_names[current_algo], font_lg};
    Button btn_bigger = {bx + 270, by, 120, 28, COL_BTN3, COL_BTN3_H, "SIZE [S]", font_lg};
    bx += 400;

    int gen_time_ms = 0;
    int hover_btn = -1;
    int running = 1;
    SDL_Event ev;

    /* generate first maze instantly */
    Uint32 t0 = SDL_GetTicks();
    maze_generate_full(&maze, current_algo);
    gen_time_ms = (int)(SDL_GetTicks() - t0);

    while (running) {
        while (SDL_PollEvent(&ev)) {
            if (ev.type == SDL_QUIT) running = 0;
            if (ev.type == SDL_KEYDOWN) {
                switch (ev.key.keysym.sym) {
                case SDLK_ESCAPE: running = 0; break;
                case SDLK_g:
                    t0 = SDL_GetTicks();
                    maze_generate_full(&maze, current_algo);
                    gen_time_ms = (int)(SDL_GetTicks() - t0);

                    break;
                case SDLK_a:
                    current_algo = (current_algo + 1) % ALG_ALDUS;
                    btn_algo.label = algo_names[current_algo];
                    t0 = SDL_GetTicks();
                    maze_generate_full(&maze, current_algo);
                    gen_time_ms = (int)(SDL_GetTicks() - t0);

                    break;
                case SDLK_s: {
                    int sizes[][2] = {{25,40},{30,50},{40,65},{50,80},{60,100},{75,120},{100,160}};
                    int nsizes = sizeof(sizes)/sizeof(sizes[0]);
                    static int size_idx = 3;
                    size_idx = (size_idx + 1) % nsizes;
                    maze_init(&maze, sizes[size_idx][0], sizes[size_idx][1]);
                    t0 = SDL_GetTicks();
                    maze_generate_full(&maze, current_algo);
                    gen_time_ms = (int)(SDL_GetTicks() - t0);

                    break;
                }
                default: break;
                }
            }
            if (ev.type == SDL_MOUSEMOTION) {
                int mx = ev.motion.x, my = ev.motion.y;
                hover_btn = -1;
                if (btn_hit(&btn_gen, mx, my)) hover_btn = 0;
                else if (btn_hit(&btn_algo, mx, my)) hover_btn = 1;
                else if (btn_hit(&btn_bigger, mx, my)) hover_btn = 2;
            }
            if (ev.type == SDL_MOUSEBUTTONDOWN && ev.button.button == SDL_BUTTON_LEFT) {
                int mx = ev.button.x, my = ev.button.y;
                if (btn_hit(&btn_gen, mx, my)) {
                    t0 = SDL_GetTicks();
                    maze_generate_full(&maze, current_algo);
                    gen_time_ms = (int)(SDL_GetTicks() - t0);

                } else if (btn_hit(&btn_algo, mx, my)) {
                    current_algo = (current_algo + 1) % ALG_ALDUS;
                    btn_algo.label = algo_names[current_algo];
                    t0 = SDL_GetTicks();
                    maze_generate_full(&maze, current_algo);
                    gen_time_ms = (int)(SDL_GetTicks() - t0);

                } else if (btn_hit(&btn_bigger, mx, my)) {
                    int sizes[][2] = {{25,40},{30,50},{40,65},{50,80},{60,100},{75,120},{100,160}};
                    int nsizes = sizeof(sizes)/sizeof(sizes[0]);
                    static int size_idx = 3;
                    size_idx = (size_idx + 1) % nsizes;
                    maze_init(&maze, sizes[size_idx][0], sizes[size_idx][1]);
                    t0 = SDL_GetTicks();
                    maze_generate_full(&maze, current_algo);
                    gen_time_ms = (int)(SDL_GetTicks() - t0);

                }
            }
        }

        /* draw */
        draw_rect(ren, 0, 0, WIN_W, WIN_H, COL_BG);

        /* HUD bg */
        draw_rect(ren, 0, 0, WIN_W, HUD_H, 0xFF161b22);

        /* buttons */
        btn_draw(ren, &btn_gen, hover_btn == 0);
        btn_draw(ren, &btn_algo, hover_btn == 1);
        btn_draw(ren, &btn_bigger, hover_btn == 2);

        /* info */
        char info[256];
        snprintf(info, sizeof(info), "%dx%d  %dms  ALGO:%s",
            maze.cols, maze.rows, gen_time_ms, algo_names[current_algo]);
        draw_text(ren, bx, by + 6, info, COL_DIM, font_sm);

        snprintf(info, sizeof(info), "G:Generate  A:Algo  S:Size  ESC:Quit");
        draw_text(ren, bx + 380, by + 6, info, 0xFF484f58, font_sm);

        /* maze */
        draw_maze(ren, &maze, maze_ox, maze_oy);

        /* border glow */
        draw_rect(ren, maze_ox - 1, maze_oy - 1, maze.cols * CELL + 2, 1, COL_WALL);
        draw_rect(ren, maze_ox - 1, maze_oy + maze.rows * CELL, maze.cols * CELL + 2, 1, COL_WALL);
        draw_rect(ren, maze_ox - 1, maze_oy - 1, 1, maze.rows * CELL + 2, COL_WALL);
        draw_rect(ren, maze_ox + maze.cols * CELL, maze_oy - 1, 1, maze.rows * CELL + 2, COL_WALL);

        SDL_RenderPresent(ren);
    }

    TTF_CloseFont(font_sm);
    TTF_CloseFont(font_lg);
    TTF_CloseFont(font_title);
    TTF_Quit();
    SDL_DestroyRenderer(ren);
    SDL_DestroyWindow(win);
    SDL_Quit();
    return 0;
}
