#include "engine/core.h"
#include "engine/input.h"
#include "engine/font.h"
#include "game/level.h"
#include "game/levelgen.h"
#include "game/editor.h"
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <SDL2/SDL.h>

#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif

/* ═══════════════════════════════════════════════════
 *  Game modes
 * ═══════════════════════════════════════════════════ */

typedef enum GameMode {
    MODE_PLAY,
    MODE_EDITOR,
    MODE_PAUSED,
} GameMode;

static Level    s_level;
static Editor   s_editor;
static GameMode s_mode         = MODE_PLAY;
static bool     s_use_procgen  = false;
static bool     s_dump_lvl     = false;
static bool     s_use_editor   = false;
static uint32_t s_gen_seed     = 0;
static char     s_edit_path[256] = {0};
static char     s_level_path[ASSET_PATH_MAX] = {0}; /* path of active play-mode level */

/* Track whether we came from the editor (for returning after test play) */
static bool s_testing_from_editor = false;

/* Station depth: increments each time we enter a new station level.
 * Drives escalating difficulty and length. */
static int s_station_depth = 0;

/* Mars Base depth: increments each generated mars_base level.
 * After 2 levels, transitions to the boss arena (mars03). */
static int s_mars_depth = 0;
#define MARS_BASE_GEN_COUNT 2

/* ── Pause menu state ── */
#define PAUSE_ITEM_COUNT 3
static int s_pause_selection = 0; /* 0=Resume, 1=Restart, 2=Quit */

static const char *theme_name(LevelTheme t) {
    switch (t) {
        case THEME_PLANET_SURFACE: return "Planet Surface";
        case THEME_PLANET_BASE:    return "Planet Base";
        case THEME_SPACE_STATION:  return "Space Station";
        case THEME_MARS_SURFACE:   return "Mars Surface";
        case THEME_MARS_BASE:      return "Mars Base";
        default:                   return "Unknown";
    }
}

/* Load a file-based level and remember its path for editor access. */
static bool load_level_file(const char *path) {
    if (!level_load(&s_level, path)) return false;
    snprintf(s_level_path, sizeof(s_level_path), "%s", path);
    return true;
}

static void load_generated_level(void) {
    LevelGenConfig config = levelgen_default_config();
    config.seed = s_gen_seed;
    config.num_segments = 6;
    config.difficulty = 0.5f;

    /* Ensure seed is non-zero so theme selection is deterministic
     * across restarts. If caller didn't set s_gen_seed (e.g. first
     * run with -gen but no -seed), snapshot time(NULL) now. */
    if (s_gen_seed == 0) s_gen_seed = (uint32_t)time(NULL);
    config.seed = s_gen_seed;

    /* Build a theme progression — start on surface, move indoors/upward.
     * Derive from seed so it varies with each regeneration and doesn't
     * depend on rand() state (which parallax generators clobber). */
    uint32_t seed_for_theme = config.seed;
    int r = (int)(seed_for_theme % 6);
    switch (r) {
        case 0:  /* Surface -> Base */
            config.themes[0] = THEME_PLANET_SURFACE;
            config.themes[1] = THEME_PLANET_SURFACE;
            config.themes[2] = THEME_PLANET_BASE;
            config.themes[3] = THEME_PLANET_BASE;
            config.themes[4] = THEME_PLANET_BASE;
            config.themes[5] = THEME_PLANET_BASE;
            config.theme_count = 6;
            break;
        case 1:  /* Base -> Station */
            config.themes[0] = THEME_PLANET_BASE;
            config.themes[1] = THEME_PLANET_BASE;
            config.themes[2] = THEME_PLANET_BASE;
            config.themes[3] = THEME_SPACE_STATION;
            config.themes[4] = THEME_SPACE_STATION;
            config.themes[5] = THEME_SPACE_STATION;
            config.theme_count = 6;
            break;
        case 2:  /* Surface -> Base -> Station (full journey) */
            config.themes[0] = THEME_PLANET_SURFACE;
            config.themes[1] = THEME_PLANET_SURFACE;
            config.themes[2] = THEME_PLANET_BASE;
            config.themes[3] = THEME_PLANET_BASE;
            config.themes[4] = THEME_SPACE_STATION;
            config.themes[5] = THEME_SPACE_STATION;
            config.theme_count = 6;
            break;
        case 3:  /* Mars Surface -> Mars Base */
            config.themes[0] = THEME_MARS_SURFACE;
            config.themes[1] = THEME_MARS_SURFACE;
            config.themes[2] = THEME_MARS_SURFACE;
            config.themes[3] = THEME_MARS_BASE;
            config.themes[4] = THEME_MARS_BASE;
            config.themes[5] = THEME_MARS_BASE;
            config.theme_count = 6;
            break;
        case 4:  /* Mars Surface -> Mars Base -> Station */
            config.themes[0] = THEME_MARS_SURFACE;
            config.themes[1] = THEME_MARS_SURFACE;
            config.themes[2] = THEME_MARS_BASE;
            config.themes[3] = THEME_MARS_BASE;
            config.themes[4] = THEME_SPACE_STATION;
            config.themes[5] = THEME_SPACE_STATION;
            config.theme_count = 6;
            break;
        case 5:  /* Single theme (derived from seed) */
        default: {
            LevelTheme single = (LevelTheme)(seed_for_theme / 6 % THEME_COUNT);
            config.themes[0] = single;
            config.theme_count = 1;
            break;
        }
    }

    printf("Theme sequence:");
    for (int i = 0; i < config.theme_count; i++) {
        printf(" %s", theme_name(config.themes[i]));
    }
    printf("\n");

    Tilemap gen_map;
    if (!levelgen_generate(&gen_map, &config)) {
        fprintf(stderr, "Failed to generate level!\n");
        g_engine.running = false;
        return;
    }

    /* Optionally dump to file for inspection */
    if (s_dump_lvl) {
        levelgen_dump_lvl(&gen_map, "assets/levels/generated.lvl");
    }

    if (!level_load_generated(&s_level, &gen_map)) {
        fprintf(stderr, "Failed to load generated level!\n");
        g_engine.running = false;
    }
    s_level_path[0] = '\0'; /* generated levels have no file path */
}

static void load_station_level(void) {
    LevelGenConfig config = levelgen_station_config(s_gen_seed, s_station_depth);
    s_station_depth++;

    printf("Generating space station level (depth=%d, gravity=%.0f, segments=%d, difficulty=%.2f)\n",
           s_station_depth, config.gravity, config.num_segments, config.difficulty);

    Tilemap gen_map;
    if (!levelgen_generate_station(&gen_map, &config)) {
        fprintf(stderr, "Failed to generate station level!\n");
        g_engine.running = false;
        return;
    }

    if (s_dump_lvl) {
        levelgen_dump_lvl(&gen_map, "assets/levels/generated_station.lvl");
    }

    if (!level_load_generated(&s_level, &gen_map)) {
        fprintf(stderr, "Failed to load station level!\n");
        g_engine.running = false;
    }
    s_level_path[0] = '\0'; /* generated levels have no file path */
}

static void load_mars_base_level(void) {
    LevelGenConfig config = levelgen_mars_base_config(s_gen_seed, s_mars_depth);
    s_mars_depth++;

    printf("Generating Mars Base level (depth=%d, gravity=%.0f, segments=%d, difficulty=%.2f)\n",
           s_mars_depth, config.gravity, config.num_segments, config.difficulty);

    Tilemap gen_map;
    if (!levelgen_generate_mars_base(&gen_map, &config)) {
        fprintf(stderr, "Failed to generate Mars Base level!\n");
        g_engine.running = false;
        return;
    }

    /* After MARS_BASE_GEN_COUNT generated levels, point exit to boss arena */
    if (s_mars_depth >= MARS_BASE_GEN_COUNT && gen_map.exit_zone_count > 0) {
        ExitZone *ez = &gen_map.exit_zones[gen_map.exit_zone_count - 1];
        snprintf(ez->target, sizeof(ez->target), "assets/levels/mars03.lvl");
    }

    if (s_dump_lvl) {
        levelgen_dump_lvl(&gen_map, "assets/levels/generated_mars_base.lvl");
    }

    if (!level_load_generated(&s_level, &gen_map)) {
        fprintf(stderr, "Failed to load Mars Base level!\n");
        g_engine.running = false;
    }
    s_level_path[0] = '\0';
}

/* ── Switch to editor mode ── */
static void enter_editor(void) {
    if (s_mode == MODE_PLAY) {
        level_free(&s_level);
    }
    s_mode = MODE_EDITOR;

    editor_init(&s_editor);
    if (s_edit_path[0]) {
        if (!editor_load(&s_editor, s_edit_path)) {
            fprintf(stderr, "Failed to load %s, creating new level\n", s_edit_path);
            editor_new_level(&s_editor, 40, 23);
        }
    } else {
        editor_new_level(&s_editor, 40, 23);
    }

    /* Change window title */
    SDL_SetWindowTitle(g_engine.window, "Jump 'n Run - Level Editor");
}

/* ── Switch to play mode (test play from editor) ── */
static void enter_test_play(void) {
    /* Save the current level to a temp file */
    editor_save_as(&s_editor, "assets/levels/_editor_test.lvl");

    s_mode = MODE_PLAY;
    s_testing_from_editor = true;

    if (!level_load(&s_level, "assets/levels/_editor_test.lvl")) {
        fprintf(stderr, "Failed to load editor test level!\n");
        /* Fall back to editor */
        s_mode = MODE_EDITOR;
        s_testing_from_editor = false;
        return;
    }

    SDL_SetWindowTitle(g_engine.window, "Jump 'n Run - Testing");
}

/* ── Return from test play to editor ── */
static void return_to_editor(void) {
    level_free(&s_level);
    s_mode = MODE_EDITOR;
    s_testing_from_editor = false;
    SDL_SetWindowTitle(g_engine.window, "Jump 'n Run - Level Editor");
}

/* ── Restart current level (file-based or generated) ── */
static void restart_level(void) {
    level_free(&s_level);
    if (s_level_path[0]) {
        if (!load_level_file(s_level_path)) {
            fprintf(stderr, "Failed to restart level: %s\n", s_level_path);
            g_engine.running = false;
        }
    } else {
        /* Generated level — regenerate with same seed. */
        load_generated_level();
    }
}

/* ═══════════════════════════════════════════════════
 *  Game callbacks
 * ═══════════════════════════════════════════════════ */

static void game_init(void) {
    if (s_use_editor) {
        enter_editor();
    } else if (s_use_procgen) {
        load_generated_level();
    } else {
        if (!load_level_file("assets/levels/moon01.lvl")) {
            fprintf(stderr, "Failed to load level!\n");
            g_engine.running = false;
        }
    }
}

/* ── Pause menu: handle input and confirm actions ── */
static void pause_update(void) {
    /* Unpause on escape */
    if (input_pressed(ACTION_PAUSE)) {
        s_mode = MODE_PLAY;
        return;
    }

    /* Navigate menu items */
    if (input_pressed(ACTION_UP)) {
        s_pause_selection--;
        if (s_pause_selection < 0) s_pause_selection = PAUSE_ITEM_COUNT - 1;
    }
    if (input_pressed(ACTION_DOWN)) {
        s_pause_selection++;
        if (s_pause_selection >= PAUSE_ITEM_COUNT) s_pause_selection = 0;
    }

    /* Confirm selection with jump or enter */
    bool confirm = input_pressed(ACTION_JUMP)
                || input_key_pressed(SDL_SCANCODE_RETURN)
                || input_key_pressed(SDL_SCANCODE_RETURN2);
    if (!confirm) return;

    switch (s_pause_selection) {
        case 0: /* Resume */
            s_mode = MODE_PLAY;
            break;
        case 1: /* Restart */
            s_mode = MODE_PLAY;
            restart_level();
            break;
        case 2: /* Quit */
            if (s_testing_from_editor) {
                return_to_editor();
            } else {
                g_engine.running = false;
            }
            break;
    }
}

static void game_update(float dt) {
    if (s_mode == MODE_EDITOR) {
        editor_update(&s_editor, dt);

        if (editor_wants_test_play(&s_editor)) {
            enter_test_play();
        }
        if (editor_wants_quit(&s_editor)) {
            g_engine.running = false;
        }
        return;
    }

    if (s_mode == MODE_PAUSED) {
        pause_update();
        return;
    }

    /* ── Play mode ── */

    /* Pause on escape (return to editor during test play) */
    if (input_pressed(ACTION_PAUSE)) {
        if (s_testing_from_editor) {
            return_to_editor();
            return;
        }
        s_pause_selection = 0;
        s_mode = MODE_PAUSED;
        return;
    }

    /* E key: enter editor from gameplay (not during test play) */
    if (!s_testing_from_editor && input_key_pressed(SDL_SCANCODE_E)) {
        /* Load the current level file into the editor if available */
        snprintf(s_edit_path, sizeof(s_edit_path), "%s", s_level_path);
        level_free(&s_level);
        enter_editor();
        return;
    }

    /* R key: regenerate level with new seed */
    static bool r_was_pressed = false;
    bool r_pressed = input_key_held(SDL_SCANCODE_R);
    if (r_pressed && !r_was_pressed) {
        printf("\n=== Regenerating level ===\n");
        level_free(&s_level);
        s_gen_seed = (uint32_t)time(NULL);
        s_use_procgen = true;
        load_generated_level();
    }
    r_was_pressed = r_pressed;

    level_update(&s_level, dt);

    /* Check for level exit transition */
    if (level_exit_triggered(&s_level)) {
        const char *target = s_level.exit_target;

        if (target[0] == '\0') {
            /* Empty target = victory / end of game */
            printf("Level complete! (no next level)\n");
            /* Loop back to the beginning, reset progression state */
            level_free(&s_level);
            s_station_depth = 0;
            s_mars_depth = 0;
            if (!load_level_file("assets/levels/moon01.lvl")) {
                g_engine.running = false;
            }
        } else if (strcmp(target, "generate") == 0) {
            /* Procedurally generated next level */
            printf("Transitioning to generated level\n");
            level_free(&s_level);
            s_gen_seed = (uint32_t)time(NULL);
            load_generated_level();
        } else if (strcmp(target, "generate:station") == 0) {
            /* Procedurally generated space station level */
            printf("Transitioning to space station level\n");
            level_free(&s_level);
            s_gen_seed = (uint32_t)time(NULL);
            load_station_level();
        } else if (strcmp(target, "generate:mars_base") == 0) {
            /* Procedurally generated Mars Base level */
            printf("Transitioning to Mars Base level\n");
            level_free(&s_level);
            s_gen_seed = (uint32_t)time(NULL);
            load_mars_base_level();
        } else {
            /* Load a specific level file */
            printf("Transitioning to: %s\n", target);
            char path[ASSET_PATH_MAX];
            snprintf(path, sizeof(path), "%s", target);
            level_free(&s_level);
            if (!load_level_file(path)) {
                fprintf(stderr, "Failed to load next level: %s\n", path);
                /* Fallback to moon01 */
                if (!load_level_file("assets/levels/moon01.lvl")) {
                    g_engine.running = false;
                }
            }
        }
    }
}

/* ── Draw the pause menu overlay on top of the frozen game frame ── */
static void pause_render(void) {
    SDL_Renderer *r = g_engine.renderer;

    /* Semi-transparent dark overlay */
    SDL_SetRenderDrawBlendMode(r, SDL_BLENDMODE_BLEND);
    SDL_SetRenderDrawColor(r, 0, 0, 0, 140);
    SDL_Rect overlay = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT};
    SDL_RenderFillRect(r, &overlay);

    /* Title */
    SDL_Color col_title = {255, 255, 255, 255};
    font_draw_text_centered(r, "PAUSED", SCREEN_HEIGHT / 2 - 40,
                            SCREEN_WIDTH, col_title);

    /* Menu items */
    static const char *items[PAUSE_ITEM_COUNT] = {
        "RESUME", "RESTART", "QUIT"
    };
    SDL_Color col_normal = {160, 160, 170, 255};
    SDL_Color col_active = {255, 220, 80, 255};

    int item_y = SCREEN_HEIGHT / 2 - 8;
    for (int i = 0; i < PAUSE_ITEM_COUNT; i++) {
        SDL_Color c = (i == s_pause_selection) ? col_active : col_normal;

        /* Draw selection indicator */
        if (i == s_pause_selection) {
            int tw = font_text_width(items[i]);
            int tx = (SCREEN_WIDTH - tw) / 2;
            font_draw_text(r, ">", tx - 8, item_y, col_active);
        }

        font_draw_text_centered(r, items[i], item_y, SCREEN_WIDTH, c);
        item_y += 14;
    }

    /* Restore blend mode */
    SDL_SetRenderDrawBlendMode(r, SDL_BLENDMODE_NONE);
}

static void game_render(float interpolation) {
    if (s_mode == MODE_EDITOR) {
        editor_render(&s_editor, interpolation);
    } else if (s_mode == MODE_PAUSED) {
        /* Render frozen game frame, then overlay the pause menu. */
        level_render(&s_level, interpolation);
        pause_render();
    } else {
        level_render(&s_level, interpolation);
    }
}

static void game_shutdown(void) {
    /* Always free both — editor may have been initialized even if we're
     * currently in play mode (e.g. shutdown during test play). editor_free
     * and level_free are safe to call on zeroed/already-freed structs. */
    if (s_mode == MODE_PLAY || s_mode == MODE_PAUSED || s_testing_from_editor) {
        level_free(&s_level);
    }
    if (s_mode == MODE_EDITOR || s_use_editor) {
        editor_free(&s_editor);
    }
}

/* ═══════════════════════════════════════════════════
 *  Main
 * ═══════════════════════════════════════════════════ */

int main(int argc, char *argv[]) {
    /* Parse command-line arguments */
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "--generate") == 0 || strcmp(argv[i], "-g") == 0) {
            s_use_procgen = true;
        } else if (strcmp(argv[i], "--dump") == 0 || strcmp(argv[i], "-d") == 0) {
            s_dump_lvl = true;
        } else if (strcmp(argv[i], "--seed") == 0 || strcmp(argv[i], "-s") == 0) {
            if (i + 1 < argc) {
                s_gen_seed = (uint32_t)atoi(argv[++i]);
            }
        } else if (strcmp(argv[i], "--edit") == 0 || strcmp(argv[i], "-e") == 0) {
            s_use_editor = true;
            /* Optional: next arg is a file path */
            if (i + 1 < argc && argv[i + 1][0] != '-') {
                strncpy(s_edit_path, argv[++i], sizeof(s_edit_path) - 1);
            }
        } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
            printf("Usage: jnr [options]\n");
            printf("  --generate, -g          Load a procedurally generated level\n");
            printf("  --dump, -d              Dump generated level to assets/levels/generated.lvl\n");
            printf("  --seed N, -s N          Set RNG seed for generation\n");
            printf("  --edit [file], -e [file]  Open level editor (optionally load a .lvl file)\n");
            printf("\nIn-game:\n");
            printf("  R                       Regenerate level with new random seed\n");
            printf("  E                       Open level editor\n");
            printf("  ESC                     Pause (or return to editor from test play)\n");
            printf("\nEditor:\n");
            printf("  1-6                     Select tool (Pencil/Eraser/Fill/Entity/Spawn/Exit)\n");
            printf("  Q/W/E                   Select layer (Collision/BG/FG)\n");
            printf("  G                       Toggle grid\n");
            printf("  V                       Toggle all-layer visibility\n");
            printf("  Arrow keys / WASD       Pan camera\n");
            printf("  Scroll wheel            Zoom in/out\n");
            printf("  Middle mouse drag       Pan camera\n");
            printf("  Left click              Paint/place (canvas) or select (palette)\n");
            printf("  Right click             Pick tile / delete entity\n");
            printf("  Ctrl+S                  Save level\n");
            printf("  Ctrl+O                  Open/load level\n");
            printf("  Ctrl++/-                Resize level width\n");
            printf("  P                       Test play level\n");
            printf("  ESC                     Quit editor\n");
            return 0;
        }
    }

    srand((unsigned)time(NULL));

#ifdef __EMSCRIPTEN__
    /* Check URL query string for ?edit or ?edit=filename */
    {
        const char *qs = emscripten_run_script_string(
            "window.location.search || ''");
        if (qs && strstr(qs, "edit")) {
            s_use_editor = true;
            /* Check for ?edit=filename */
            const char *eq = strstr(qs, "edit=");
            if (eq) {
                eq += 5; /* skip "edit=" */
                /* Copy until & or end of string */
                int len = 0;
                while (eq[len] && eq[len] != '&' && len < (int)sizeof(s_edit_path) - 1) {
                    s_edit_path[len] = eq[len];
                    len++;
                }
                s_edit_path[len] = '\0';
            }
        }
    }
#endif

    if (!engine_init()) {
        return 1;
    }

    engine_set_callbacks((GameCallbacks){
        .init     = game_init,
        .update   = game_update,
        .render   = game_render,
        .shutdown = game_shutdown,
    });

    engine_run();
    engine_shutdown();

    return 0;
}
