#include "icon_font.h"

#include "app_settings.h"
#include "../../framework/app_base.h"
#include "imgui.h"

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>

#ifndef FN_CPP_ROOT
#define FN_CPP_ROOT ""
#endif

namespace fn_ui {

namespace {

bool g_text_loaded   = false;
bool g_tabler_loaded = false;

bool file_exists(const char* path) {
    if (!path || !*path) return false;
    if (FILE* f = std::fopen(path, "rb")) { std::fclose(f); return true; }
    return false;
}

// Busca un asset (TTF) en las rutas estandar. Devuelve la primera
// ruta valida o vacio.
//
// Orden:
//   1. fn::asset_path(filename)            <exe_dir>/assets/<filename>
//   2. ./<filename>                        cwd directo (legacy)
//   3. ./assets/<filename>                 cwd subdir (legacy / dev)
//   4. $FN_ASSETS_DIR/<filename>           override env
//   5. ${FN_CPP_ROOT}/<repo_subpath>       fuente del repo (dev sin build)
std::string find_asset(const char* filename, const char* repo_subpath) {
    std::string p;
    p = fn::asset_path(filename);              if (file_exists(p.c_str())) return p;
    p = std::string("./") + filename;          if (file_exists(p.c_str())) return p;
    p = std::string("./assets/") + filename;   if (file_exists(p.c_str())) return p;
    if (const char* env = std::getenv("FN_ASSETS_DIR")) {
        p = std::string(env) + "/" + filename;
        if (file_exists(p.c_str())) return p;
    }
    if (std::strlen(FN_CPP_ROOT) > 0 && repo_subpath) {
        p = std::string(FN_CPP_ROOT) + "/" + repo_subpath;
        if (file_exists(p.c_str())) return p;
    }
    return std::string();
}

const char* repo_subpath_for(FontId id) {
    switch (id) {
        case FontId::Karla:       return "vendor/imgui/misc/fonts/Karla-Regular.ttf";
        case FontId::Roboto:      return "vendor/imgui/misc/fonts/Roboto-Medium.ttf";
        case FontId::DroidSans:   return "vendor/imgui/misc/fonts/DroidSans.ttf";
        case FontId::Cousine:     return "vendor/imgui/misc/fonts/Cousine-Regular.ttf";
        case FontId::ProggyClean: return ""; // bitmap default
    }
    return "";
}

} // namespace

void load_fonts_from_settings() {
    ImGuiIO& io = ImGui::GetIO();
    io.Fonts->Clear();

    const AppSettings& s = settings();
    const float size_px = s.font_size_px;

    // Extended glyph range for the text font. ImGui's default is Latin-1 only,
    // which leaves bullets/dots/arrows/checkmarks as `?`. Adds:
    //   General Punctuation     (em-dash, ellipsis, smart quotes)
    //   Geometric Shapes        (● ▲ ■ ▼ ▶ ◀ — used by data_table Dots renderer)
    //   Miscellaneous Symbols   (★ ☆ ☑ ☒ ♦)
    //   Dingbats                (✓ ✔ ✗ ✘ ✱ ❤)
    //   Misc Symbols & Arrows   (⬆ ⬇ ⬅ ➡)
    //   Geometric Shapes Ext    (🭨 etc.) — kept narrow to avoid bloat
    static const ImWchar text_ranges[] = {
        0x0020, 0x00FF, // Basic Latin + Latin-1
        0x2010, 0x2027, // General Punctuation (em-dash, ellipsis, quotes)
        0x25A0, 0x25FF, // Geometric Shapes (● ▲ ■ ◆ ...)
        0x2600, 0x26FF, // Misc Symbols (★ ♥ ☑)
        0x2700, 0x27BF, // Dingbats (✓ ✗ ✱)
        0x2B00, 0x2BFF, // Arrows + Misc Symbols (⬆ ⬇ ⬅ ➡)
        0,
    };

    // 1. Texto.
    g_text_loaded = false;
    if (s.font_id == FontId::ProggyClean) {
        // Bitmap default: ignora size_px (ProggyClean es 13 px nativo).
        io.Fonts->AddFontDefault();
    } else {
        const char* fname = font_filename(s.font_id);
        std::string ttf = find_asset(fname, repo_subpath_for(s.font_id));
        if (!ttf.empty()) {
            ImFontConfig cfg;
            cfg.OversampleH = 2;
            cfg.OversampleV = 1;
            cfg.PixelSnapH = false;
            if (io.Fonts->AddFontFromFileTTF(ttf.c_str(), size_px, &cfg, text_ranges)) {
                g_text_loaded = true;
            } else {
                std::fprintf(stderr, "[fn_ui] AddFontFromFileTTF fallo (%s)\n", ttf.c_str());
                io.Fonts->AddFontDefault();
            }
        } else {
            std::fprintf(stderr,
                "[fn_ui] %s no encontrado — fallback a ProggyClean. "
                "Buscado en: ./%s, ./assets/, $FN_ASSETS_DIR, %s/vendor/imgui/misc/fonts/\n",
                fname, fname, FN_CPP_ROOT[0] ? FN_CPP_ROOT : "(FN_CPP_ROOT no definido)");
            io.Fonts->AddFontDefault();
        }
    }

    // 2. Iconos Tabler — mergea sobre la fuente activa.
    std::string ico = find_asset("tabler-icons.ttf", "vendor/tabler-icons/tabler-icons.ttf");
    if (ico.empty()) {
        std::fprintf(stderr,
            "[fn_ui] tabler-icons.ttf no encontrado — los TI_* saldran como cuadritos. "
            "Buscado en: ./tabler-icons.ttf, ./assets/, $FN_ASSETS_DIR, %s/vendor/tabler-icons/\n",
            FN_CPP_ROOT[0] ? FN_CPP_ROOT : "(FN_CPP_ROOT no definido)");
        g_tabler_loaded = false;
        return;
    }

    static const ImWchar tabler_ranges[] = {0xE000, 0xFCFF, 0};
    // Tabler matchea el tamaño de texto, salvo en ProggyClean (13 px) donde lo
    // forzamos a 13 tambien para no romper line-height.
    const float tabler_px = (s.font_id == FontId::ProggyClean) ? 13.0f : size_px;

    ImFontConfig icfg;
    icfg.MergeMode = true;
    icfg.PixelSnapH = true;
    icfg.OversampleH = icfg.OversampleV = 1;
    icfg.GlyphMinAdvanceX = tabler_px;
    icfg.GlyphOffset.y = 1.0f;
    if (io.Fonts->AddFontFromFileTTF(ico.c_str(), tabler_px, &icfg, tabler_ranges)) {
        g_tabler_loaded = true;
    } else {
        std::fprintf(stderr, "[fn_ui] AddFontFromFileTTF tabler fallo (%s)\n", ico.c_str());
        g_tabler_loaded = false;
    }
}

bool text_font_loaded()   { return g_text_loaded; }
bool tabler_font_loaded() { return g_tabler_loaded; }

} // namespace fn_ui
