#include "PocketDungeon.h"
#include <cstdio>
extern "C" {
#include <tt_app.h>
}

namespace PocketDungeon {

static constexpr uint32_t TICK_MS = 200;
static constexpr const char* PREF_BEST_FLOOR = "bestFloor";
static constexpr const char* PREF_BEST_GOLD = "bestGold";

static constexpr uint32_t COLOR_BG = 0x10111A;
static constexpr uint32_t COLOR_PANEL = 0x1D2030;
static constexpr uint32_t COLOR_ACCENT = 0x365CF5;
static constexpr uint32_t COLOR_OVERLAY = 0x000000;

void PocketDungeon::onShow(AppHandle app, lv_obj_t* parent) {
    lv_obj_remove_flag(parent, LV_OBJ_FLAG_SCROLLABLE);
    lv_obj_set_style_pad_all(parent, 0, LV_PART_MAIN);
    lv_obj_set_style_bg_color(parent, lv_color_hex(COLOR_BG), LV_PART_MAIN);
    root = parent;
    model.setBests(static_cast<uint16_t>(prefs.getInt(PREF_BEST_FLOOR, 1)), static_cast<uint16_t>(prefs.getInt(PREF_BEST_GOLD, 0)));
    model.reset(lv_tick_get(), true);
    if (sfx == nullptr) {
        sfx = new SfxEngine();
        if (sfx->start()) sfx->applyVolumePreset(SfxEngine::VolumePreset::Quiet);
    }
    phase = AppPhase::Intro;
    onEnter(parent);
    loop.start(TICK_MS, this);
    (void) app;
}

void PocketDungeon::onHide(AppHandle app) {
    (void) app;
    loop.stop();
    onExit();
    saveBests();
    if (sfx) { sfx->stop(); delete sfx; sfx = nullptr; }
    root = nullptr;
}

void PocketDungeon::onEnter(lv_obj_t* sceneRoot) {
    root = sceneRoot;
    GameKit::attachInput(root, this);
    showIntro();
    lastResult = MoveResult::None;
}

void PocketDungeon::onExit() { clearIntro(); clearGame(); }

void PocketDungeon::attachInputToCurrent() {
    if (root) GameKit::attachInput(root, this);
    if (introContainer) {
        lv_obj_add_flag(introContainer, LV_OBJ_FLAG_CLICKABLE);
        lv_obj_add_flag(introContainer, LV_OBJ_FLAG_GESTURE_BUBBLE);
        GameKit::attachInput(introContainer, this);
    }
    if (gameContainer) {
        lv_obj_add_flag(gameContainer, LV_OBJ_FLAG_CLICKABLE);
        lv_obj_add_flag(gameContainer, LV_OBJ_FLAG_GESTURE_BUBBLE);
        GameKit::attachInput(gameContainer, this);
        lv_obj_add_event_cb(gameContainer, onGameContainerLongPress, LV_EVENT_LONG_PRESSED, this);
    }
    if (pauseOverlay) {
        lv_obj_add_flag(pauseOverlay, LV_OBJ_FLAG_CLICKABLE);
        GameKit::attachInput(pauseOverlay, this);
    }
}

void PocketDungeon::clearIntro() { if (introContainer) { lv_obj_delete(introContainer); introContainer = nullptr; } }
void PocketDungeon::clearGame() {
    if (pauseOverlay) { lv_obj_delete(pauseOverlay); pauseOverlay = nullptr; }
    if (gameContainer) { lv_obj_delete(gameContainer); gameContainer = nullptr; }
}

void PocketDungeon::showIntro() {
    clearGame(); clearIntro();
    phase = AppPhase::Intro;
    introContainer = lv_obj_create(root);
    lv_obj_set_size(introContainer, LV_PCT(100), LV_PCT(100));
    lv_obj_set_style_pad_all(introContainer, 0, LV_PART_MAIN);
    lv_obj_set_style_border_width(introContainer, 0, LV_PART_MAIN);
    lv_obj_set_style_bg_color(introContainer, lv_color_hex(COLOR_BG), LV_PART_MAIN);
    lv_obj_remove_flag(introContainer, LV_OBJ_FLAG_SCROLLABLE);
    lv_obj_set_flex_flow(introContainer, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_flex_align(introContainer, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
    lv_obj_set_style_pad_row(introContainer, 6, LV_PART_MAIN);
    lv_obj_add_flag(introContainer, LV_OBJ_FLAG_CLICKABLE);
    lv_obj_add_flag(introContainer, LV_OBJ_FLAG_GESTURE_BUBBLE);
    renderIntro();
    attachInputToCurrent();
}

void PocketDungeon::showGame() {
    clearIntro(); clearGame();
    phase = AppPhase::Playing;
    gameContainer = lv_obj_create(root);
    lv_obj_set_size(gameContainer, LV_PCT(100), LV_PCT(100));
    lv_obj_set_style_pad_all(gameContainer, 0, LV_PART_MAIN);
    lv_obj_set_style_border_width(gameContainer, 0, LV_PART_MAIN);
    lv_obj_set_style_bg_opa(gameContainer, LV_OPA_TRANSP, LV_PART_MAIN);
    lv_obj_remove_flag(gameContainer, LV_OBJ_FLAG_SCROLLABLE);
    lv_obj_add_flag(gameContainer, LV_OBJ_FLAG_CLICKABLE);
    lv_obj_add_flag(gameContainer, LV_OBJ_FLAG_GESTURE_BUBBLE);
    renderer.create(gameContainer);
    lv_obj_t* pauseBtn = lv_btn_create(gameContainer);
    lv_obj_set_size(pauseBtn, 32, 26);
    lv_obj_set_style_bg_color(pauseBtn, lv_color_hex(0x2A2D40), LV_PART_MAIN);
    lv_obj_set_style_radius(pauseBtn, 6, LV_PART_MAIN);
    lv_obj_set_style_pad_all(pauseBtn, 0, LV_PART_MAIN);
    lv_obj_align(pauseBtn, LV_ALIGN_TOP_RIGHT, -6, 4);
    lv_obj_add_event_cb(pauseBtn, onExitButtonClicked, LV_EVENT_CLICKED, this);
    lv_obj_t* pauseLbl = lv_label_create(pauseBtn);
    lv_label_set_text(pauseLbl, LV_SYMBOL_CLOSE);
    lv_obj_center(pauseLbl);
    lastResult = MoveResult::None;
    attachInputToCurrent();
    render();
    if (sfx) sfx->play(SfxId::Warp);
}

void PocketDungeon::showPause() {
    if (pauseOverlay) return;
    phase = AppPhase::Paused;
    pauseOverlay = lv_obj_create(root);
    lv_obj_set_size(pauseOverlay, LV_PCT(100), LV_PCT(100));
    lv_obj_set_style_pad_all(pauseOverlay, 12, LV_PART_MAIN);
    lv_obj_set_style_border_width(pauseOverlay, 0, LV_PART_MAIN);
    lv_obj_set_style_bg_color(pauseOverlay, lv_color_hex(COLOR_OVERLAY), LV_PART_MAIN);
    lv_obj_set_style_bg_opa(pauseOverlay, LV_OPA_70, LV_PART_MAIN);
    lv_obj_remove_flag(pauseOverlay, LV_OBJ_FLAG_SCROLLABLE);
    lv_obj_set_flex_flow(pauseOverlay, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_flex_align(pauseOverlay, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
    lv_obj_set_style_pad_row(pauseOverlay, 10, LV_PART_MAIN);
    lv_obj_add_flag(pauseOverlay, LV_OBJ_FLAG_CLICKABLE);
    lv_obj_t* box = lv_obj_create(pauseOverlay);
    lv_obj_set_width(box, LV_PCT(80));
    lv_obj_set_height(box, LV_SIZE_CONTENT);
    lv_obj_set_style_pad_all(box, 14, LV_PART_MAIN);
    lv_obj_set_style_bg_color(box, lv_color_hex(COLOR_PANEL), LV_PART_MAIN);
    lv_obj_set_style_border_width(box, 0, LV_PART_MAIN);
    lv_obj_set_style_radius(box, 12, LV_PART_MAIN);
    lv_obj_set_flex_flow(box, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_flex_align(box, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
    lv_obj_set_style_pad_row(box, 10, LV_PART_MAIN);
    lv_obj_remove_flag(box, LV_OBJ_FLAG_SCROLLABLE);
    lv_obj_t* t = lv_label_create(box);
    lv_label_set_text(t, "Paused");
    lv_obj_set_style_text_color(t, lv_color_white(), LV_PART_MAIN);
    lv_obj_t* resumeBtn = lv_btn_create(box);
    lv_obj_set_width(resumeBtn, LV_PCT(100));
    lv_obj_set_height(resumeBtn, 38);
    lv_obj_set_style_bg_color(resumeBtn, lv_color_hex(COLOR_ACCENT), LV_PART_MAIN);
    lv_obj_set_style_radius(resumeBtn, 10, LV_PART_MAIN);
    lv_obj_add_event_cb(resumeBtn, onResumeButtonClicked, LV_EVENT_CLICKED, this);
    lv_obj_t* rl = lv_label_create(resumeBtn);
    lv_label_set_text(rl, "Resume");
    lv_obj_center(rl);
    lv_obj_t* exitBtn = lv_btn_create(box);
    lv_obj_set_width(exitBtn, LV_PCT(100));
    lv_obj_set_height(exitBtn, 36);
    lv_obj_set_style_bg_color(exitBtn, lv_color_hex(0x2E303F), LV_PART_MAIN);
    lv_obj_set_style_radius(exitBtn, 10, LV_PART_MAIN);
    lv_obj_add_event_cb(exitBtn, onExitButtonClicked, LV_EVENT_CLICKED, this);
    lv_obj_t* el = lv_label_create(exitBtn);
    lv_label_set_text(el, "Exit Game");
    lv_obj_center(el);
    if (sfx) sfx->play(SfxId::MenuOpen);
    attachInputToCurrent();
}

void PocketDungeon::hidePause() {
    if (pauseOverlay) { lv_obj_delete(pauseOverlay); pauseOverlay = nullptr; }
    phase = AppPhase::Playing;
    if (sfx) sfx->play(SfxId::MenuClose);
    attachInputToCurrent();
    render();
}

void PocketDungeon::exitGame() {
    saveBests();
    if (sfx) sfx->play(SfxId::Cancel);
    tt_app_stop();
}

void PocketDungeon::renderIntro() {
    if (!introContainer) return;
    lv_obj_t* titleRow = lv_obj_create(introContainer);
    lv_obj_set_width(titleRow, LV_PCT(100));
    lv_obj_set_height(titleRow, LV_SIZE_CONTENT);
    lv_obj_set_style_pad_all(titleRow, 6, LV_PART_MAIN);
    lv_obj_set_style_border_width(titleRow, 0, LV_PART_MAIN);
    lv_obj_set_style_bg_opa(titleRow, LV_OPA_TRANSP, LV_PART_MAIN);
    lv_obj_remove_flag(titleRow, LV_OBJ_FLAG_SCROLLABLE);
    lv_obj_set_flex_flow(titleRow, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_flex_align(titleRow, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
    lv_obj_set_style_pad_row(titleRow, 2, LV_PART_MAIN);
    lv_obj_t* title = lv_label_create(titleRow);
    lv_label_set_text(title, "POCKET DUNGEON");
    lv_obj_set_style_text_color(title, lv_color_hex(0xF8F3E6), LV_PART_MAIN);
    lv_obj_t* subtitle = lv_label_create(titleRow);
    lv_label_set_text(subtitle, "Tiny paper roguelike");
    lv_obj_set_style_text_color(subtitle, lv_color_hex(0x8A9FBF), LV_PART_MAIN);
    char bestBuf[64];
    std::snprintf(bestBuf, sizeof(bestBuf), "Best F%u G%u", model.getBestFloor(), model.getBestGold());
    lv_obj_t* best = lv_label_create(titleRow);
    lv_label_set_text(best, bestBuf);
    lv_obj_set_style_text_color(best, lv_color_hex(0x7A8196), LV_PART_MAIN);

    lv_obj_t* mainRow = lv_obj_create(introContainer);
    lv_obj_set_width(mainRow, LV_PCT(100));
    lv_obj_set_flex_grow(mainRow, 1);
    lv_obj_set_style_pad_all(mainRow, 4, LV_PART_MAIN);
    lv_obj_set_style_pad_column(mainRow, 8, LV_PART_MAIN);
    lv_obj_set_style_pad_bottom(mainRow, 8, LV_PART_MAIN);
    lv_obj_set_style_border_width(mainRow, 0, LV_PART_MAIN);
    lv_obj_set_style_bg_opa(mainRow, LV_OPA_TRANSP, LV_PART_MAIN);
    lv_obj_set_flex_flow(mainRow, LV_FLEX_FLOW_ROW);
    lv_obj_set_flex_align(mainRow, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START);
    lv_obj_remove_flag(mainRow, LV_OBJ_FLAG_SCROLLABLE);

    lv_obj_t* leftCol = lv_obj_create(mainRow);
    lv_obj_set_flex_grow(leftCol, 1);
    lv_obj_set_height(leftCol, LV_PCT(100));
    lv_obj_set_style_pad_all(leftCol, 0, LV_PART_MAIN);
    lv_obj_set_style_pad_right(leftCol, 4, LV_PART_MAIN);
    lv_obj_set_style_pad_bottom(leftCol, 6, LV_PART_MAIN);
    lv_obj_set_style_border_width(leftCol, 0, LV_PART_MAIN);
    lv_obj_set_style_bg_opa(leftCol, LV_OPA_TRANSP, LV_PART_MAIN);
    lv_obj_set_flex_flow(leftCol, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_style_pad_row(leftCol, 8, LV_PART_MAIN);
    lv_obj_add_flag(leftCol, LV_OBJ_FLAG_SCROLLABLE);

    auto makeCard = [](lv_obj_t* parent, const char* tTitle, const char* body, uint32_t col) {
        lv_obj_t* card = lv_obj_create(parent);
        lv_obj_set_width(card, LV_PCT(100));
        lv_obj_set_height(card, LV_SIZE_CONTENT);
        lv_obj_set_style_pad_all(card, 9, LV_PART_MAIN);
        lv_obj_set_style_pad_row(card, 3, LV_PART_MAIN);
        lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN);
        lv_obj_set_style_border_width(card, 0, LV_PART_MAIN);
        lv_obj_set_style_radius(card, 10, LV_PART_MAIN);
        lv_obj_set_flex_flow(card, LV_FLEX_FLOW_COLUMN);
        lv_obj_remove_flag(card, LV_OBJ_FLAG_SCROLLABLE);
        lv_obj_t* t = lv_label_create(card);
        lv_label_set_text(t, tTitle);
        lv_obj_set_style_text_color(t, lv_color_hex(col), LV_PART_MAIN);
        lv_obj_t* b = lv_label_create(card);
        lv_label_set_text(b, body);
        lv_obj_set_style_text_color(b, lv_color_hex(0xD9DDE8), LV_PART_MAIN);
        lv_label_set_long_mode(b, LV_LABEL_LONG_WRAP);
        lv_obj_set_width(b, LV_PCT(100));
        return card;
    };

    makeCard(leftCol, "GOAL", "Reach deepest floor.\nCollect gold, survive.", 0xD99B23);
    makeCard(leftCol, "CONTROLS", "Arrows / WASD / Swipe\nTap quadrant = move\nQ / ESC = exit", 0x6D8CFF);

    lv_obj_t* rightRail = lv_obj_create(mainRow);
    lv_obj_set_width(rightRail, 44);
    lv_obj_set_height(rightRail, LV_SIZE_CONTENT);
    lv_obj_set_style_pad_all(rightRail, 0, LV_PART_MAIN);
    lv_obj_set_style_pad_row(rightRail, 10, LV_PART_MAIN);
    lv_obj_set_style_border_width(rightRail, 0, LV_PART_MAIN);
    lv_obj_set_style_bg_opa(rightRail, LV_OPA_TRANSP, LV_PART_MAIN);
    lv_obj_set_flex_flow(rightRail, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_flex_align(rightRail, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
    lv_obj_remove_flag(rightRail, LV_OBJ_FLAG_SCROLLABLE);

    auto makeIconBtn = [](lv_obj_t* parent, const char* symbol, uint32_t bg, lv_event_cb_t cb, void* user, int s) -> lv_obj_t* {
        lv_obj_t* btn = lv_btn_create(parent);
        lv_obj_set_size(btn, s, s);
        lv_obj_set_style_bg_color(btn, lv_color_hex(bg), LV_PART_MAIN);
        lv_obj_set_style_radius(btn, s / 3, LV_PART_MAIN);
        lv_obj_set_style_pad_all(btn, 0, LV_PART_MAIN);
        lv_obj_add_event_cb(btn, cb, LV_EVENT_CLICKED, user);
        lv_obj_t* lbl = lv_label_create(btn);
        lv_label_set_text(lbl, symbol);
        lv_obj_center(lbl);
        lv_obj_set_style_text_color(lbl, lv_color_white(), LV_PART_MAIN);
        return btn;
    };

    makeIconBtn(rightRail, LV_SYMBOL_PLAY, COLOR_ACCENT, onStartButtonClicked, this, 38);
    makeIconBtn(rightRail, LV_SYMBOL_CLOSE, 0x252836, onExitButtonClicked, this, 30);
}

void PocketDungeon::onStartButtonClicked(lv_event_t* e) { auto* s = static_cast<PocketDungeon*>(lv_event_get_user_data(e)); if (s) s->showGame(); lv_event_stop_bubbling(e); }
void PocketDungeon::onResumeButtonClicked(lv_event_t* e) { auto* s = static_cast<PocketDungeon*>(lv_event_get_user_data(e)); if (s) s->hidePause(); lv_event_stop_bubbling(e); }
void PocketDungeon::onExitButtonClicked(lv_event_t* e) { auto* s = static_cast<PocketDungeon*>(lv_event_get_user_data(e)); if (s) s->exitGame(); lv_event_stop_bubbling(e); }
void PocketDungeon::onGameContainerLongPress(lv_event_t* e) { auto* s = static_cast<PocketDungeon*>(lv_event_get_user_data(e)); if (s && s->phase==AppPhase::Playing) s->showPause(); }

void PocketDungeon::onInput(const GameKit::InputEvent& input) {
    if (input.kind == GameKit::InputKind::Cancel || input.kind == GameKit::InputKind::Menu) { exitGame(); return; }
    if (phase == AppPhase::Intro) { if (input.kind != GameKit::InputKind::Unknown) showGame(); return; }
    if (phase == AppPhase::Paused) { if (input.kind != GameKit::InputKind::Unknown) hidePause(); return; }
    int dx=0, dy=0;
    switch (input.kind) {
        case GameKit::InputKind::Up: dy=-1; break;
        case GameKit::InputKind::Down: dy=1; break;
        case GameKit::InputKind::Left: dx=-1; break;
        case GameKit::InputKind::Right: dx=1; break;
        default: break;
    }
    if (model.getState().gameOver) {
        if (dx!=0||dy!=0||input.kind==GameKit::InputKind::Confirm||input.kind==GameKit::InputKind::Touch) {
            model.reset(lv_tick_get(), true); lastResult=MoveResult::None; render();
        }
        return;
    }
    if (dx!=0||dy!=0) {
        lastResult=model.movePlayer(dx,dy);
        playResultSound(lastResult);
        if (lastResult==MoveResult::Dead) saveBests();
        render();
    }
}

void PocketDungeon::onTick(const GameKit::Tick& tick){ (void)tick; }
void PocketDungeon::render(){
    if (phase==AppPhase::Intro||phase==AppPhase::Paused) return;
    if (!gameContainer) return;
    renderer.render(model, lastResult);
}
void PocketDungeon::saveBests(){
    prefs.putInt(PREF_BEST_FLOOR, model.getBestFloor());
    prefs.putInt(PREF_BEST_GOLD, model.getBestGold());
}
void PocketDungeon::playResultSound(MoveResult r){
    if (!sfx) return;
    switch(r){
        case MoveResult::Attacked: sfx->play(SfxId::Hurt); break;
        case MoveResult::Treasure: sfx->play(SfxId::Coin); break;
        case MoveResult::Stairs: sfx->play(SfxId::Warp); break;
        case MoveResult::Dead: sfx->play(SfxId::GameOver); break;
        case MoveResult::Moved: sfx->play(SfxId::Blip); break;
        case MoveResult::Blocked: sfx->play(SfxId::Error); break;
        default: break;
    }
}

} // namespace PocketDungeon
