// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3

#include "typography.h"
#include "../layout.h"
#include "../schema/ui_schema.h"
#include <cstring>
#include <cstdio>
#include <cctype>
#include <string>

// Embedded font data (via INCBIN – assembler .incbin directive)
#include "../embedded/embedded_fonts.h"      // g_ubuntu_*_data/size, g_material_icons_*
#include "../embedded/IconsMaterialDesign.h" // Icon codepoint defines
#include "../../util/logger.h"

#ifdef DRAGONX_HAVE_FREETYPE
#include "imgui_internal.h"                   // ImFontAtlasGetFontLoaderForStbTruetype
#include "misc/freetype/imgui_freetype.h"     // ImGuiFreeType::GetFontLoader + LoadColor flag
#endif

namespace dragonx {
namespace ui {
namespace material {

// ============================================================================
// Type Scale Specifications
// ============================================================================
// Font sizes are configured in ui/layout.h for easy global adjustment.
// Letter spacing, line height and text-transform are loaded from ui.toml
// via the UISchema typography section.
// Font sizes come from UISchema (JSON-configurable) via Layout:: accessors.

const TypeSpec* Typography::getTypeSpecs()
{
    // Rebuild each call to pick up hot-reloaded values
    static TypeSpec specs[kNumStyles];

    // Helper: read typography entry from UISchema
    auto& S = schema::UI();
    auto tspec = [&](const char* key, float defLS, float defLH, bool defUpper) -> TypeSpec {
        auto e = S.drawElement("typography", key);
        float ls = e.getFloat("letter-spacing", defLS);
        float lh = e.getFloat("line-height", defLH);
        // text-transform stored as extraColors (string map)
        bool upper = defUpper;
        auto it = e.extraColors.find("text-transform");
        if (it != e.extraColors.end()) {
            upper = (it->second == "uppercase");
        }
        return TypeSpec{0.0f, ls, lh, upper};
    };

    // Font size comes from Layout:: accessors; typography from UISchema
    auto ts0  = tspec("h1",        -1.0f,  1.167f, false);
    auto ts1  = tspec("h2",        -0.5f,  1.2f,   false);
    auto ts2  = tspec("h3",         0.0f,  1.167f, false);
    auto ts3  = tspec("h4",         0.25f, 1.235f, false);
    auto ts4  = tspec("h5",         0.0f,  1.334f, false);
    auto ts5  = tspec("h6",         0.15f, 1.6f,   false);
    auto ts6  = tspec("subtitle1",  0.15f, 1.75f,  false);
    auto ts7  = tspec("subtitle2",  0.1f,  1.57f,  false);
    auto ts8  = tspec("body1",      0.5f,  1.5f,   false);
    auto ts9  = tspec("body2",      0.25f, 1.43f,  false);
    auto ts10 = tspec("button",     1.25f, 1.75f,  true);
    auto ts11 = tspec("caption",    0.4f,  1.66f,  false);
    auto ts12 = tspec("overline",   1.5f,  2.66f,  true);

    specs[0]  = { Layout::kFontH1(),        ts0.letterSpacing,  ts0.lineHeight,  ts0.uppercase  };
    specs[1]  = { Layout::kFontH2(),        ts1.letterSpacing,  ts1.lineHeight,  ts1.uppercase  };
    specs[2]  = { Layout::kFontH3(),        ts2.letterSpacing,  ts2.lineHeight,  ts2.uppercase  };
    specs[3]  = { Layout::kFontH4(),        ts3.letterSpacing,  ts3.lineHeight,  ts3.uppercase  };
    specs[4]  = { Layout::kFontH5(),        ts4.letterSpacing,  ts4.lineHeight,  ts4.uppercase  };
    specs[5]  = { Layout::kFontH6(),        ts5.letterSpacing,  ts5.lineHeight,  ts5.uppercase  };
    specs[6]  = { Layout::kFontSubtitle1(), ts6.letterSpacing,  ts6.lineHeight,  ts6.uppercase  };
    specs[7]  = { Layout::kFontSubtitle2(), ts7.letterSpacing,  ts7.lineHeight,  ts7.uppercase  };
    specs[8]  = { Layout::kFontBody1(),     ts8.letterSpacing,  ts8.lineHeight,  ts8.uppercase  };
    specs[9]  = { Layout::kFontBody2(),     ts9.letterSpacing,  ts9.lineHeight,  ts9.uppercase  };
    specs[10] = { Layout::kFontButton(),    ts10.letterSpacing, ts10.lineHeight, ts10.uppercase };
    specs[11] = { Layout::kFontCaption(),   ts11.letterSpacing, ts11.lineHeight, ts11.uppercase };
    specs[12] = { Layout::kFontOverline(),  ts12.letterSpacing, ts12.lineHeight, ts12.uppercase };
    specs[13] = { Layout::kFontButtonSm(),  ts10.letterSpacing, ts10.lineHeight, ts10.uppercase };
    specs[14] = { Layout::kFontButtonLg(),  ts10.letterSpacing, ts10.lineHeight, ts10.uppercase };
    return specs;
}

// ============================================================================
// Typography Implementation
// ============================================================================

Typography& Typography::instance()
{
    static Typography s_instance;
    return s_instance;
}

bool Typography::reload(ImGuiIO& io, float dpiScale)
{
    if (!loaded_) {
        return load(io, dpiScale);
    }
    DEBUG_LOGF("Typography: Reloading fonts for DPI scale %.2f\n", dpiScale);
    // Clear existing fonts and reload
    io.Fonts->Clear();
    loaded_ = false;
    for (int i = 0; i < kNumStyles; ++i) fonts_[i] = nullptr;
    for (int i = 0; i < kNumIconSizes; ++i) iconFonts_[i] = nullptr;
    for (int i = 0; i < kNumIconSizes; ++i) pickaxeFonts_[i] = nullptr;
    return load(io, dpiScale);
}

bool Typography::load(ImGuiIO& io, float dpiScale)
{
    if (loaded_) {
        DEBUG_LOGF("Typography: Already loaded\n");
        return true;
    }
    
    dpiScale_ = dpiScale;
    
    // Multiply by dpiScale so fonts are the correct physical size.
    // On Windows Per-Monitor DPI v2, SDL3 coordinates are physical pixels
    // and DisplayFramebufferScale is 1.0 (no automatic upscaling).
    // The window is resized by dpiScale in main.cpp so that fonts at
    // size*dpiScale fit proportionally (no overflow).
    // Layout::userFontScale() is the user-chosen accessibility multiplier
    // (1.0–3.0) persisted in Settings; it makes glyphs physically larger
    // without any bitmap up-scaling (sharp at every size).
    float scale = dpiScale * Layout::kFontScale() * Layout::userFontScale();
    DEBUG_LOGF("Typography: Loading Material Design type scale (DPI: %.2f, fontScale: %.2f, userFontScale: %.2f, combined: %.2f)\n",
               dpiScale, Layout::kFontScale(), Layout::userFontScale(), scale);

#ifdef DRAGONX_HAVE_FREETYPE
    // Choose the atlas font loader BEFORE any font is added: FreeType (required to rasterize COLR
    // color-emoji glyphs) when color emoji is enabled, else the default stb_truetype loader. Toggling
    // the setting + reload() flips this cleanly.
    io.Fonts->SetFontLoader(color_emoji_ ? ImGuiFreeType::GetFontLoader()
                                         : ImFontAtlasGetFontLoaderForStbTruetype());
    DEBUG_LOGF("Typography: font loader = %s\n", color_emoji_ ? "FreeType (color emoji)" : "stb_truetype");
#endif

    // For ImGui, we need to load fonts at specific pixel sizes.
    // Font sizes come from Layout:: accessors (backed by UISchema JSON)
    
    // Load fonts in order of TypeStyle enum
    
    // H1: Light
    fonts_[0] = loadFont(io, kWeightLight, Layout::kFontH1() * scale, "H1");
    
    // H2: Light
    fonts_[1] = loadFont(io, kWeightLight, Layout::kFontH2() * scale, "H2");
    
    // H3: Regular
    fonts_[2] = loadFont(io, kWeightRegular, Layout::kFontH3() * scale, "H3");
    
    // H4: Regular
    fonts_[3] = loadFont(io, kWeightRegular, Layout::kFontH4() * scale, "H4");
    
    // H5: Regular
    fonts_[4] = loadFont(io, kWeightRegular, Layout::kFontH5() * scale, "H5");
    
    // H6: Medium
    fonts_[5] = loadFont(io, kWeightMedium, Layout::kFontH6() * scale, "H6");
    
    // Subtitle1: Regular
    fonts_[6] = loadFont(io, kWeightRegular, Layout::kFontSubtitle1() * scale, "Subtitle1");
    
    // Subtitle2: Medium
    fonts_[7] = loadFont(io, kWeightMedium, Layout::kFontSubtitle2() * scale, "Subtitle2");
    
    // Body1: Regular (shares with Subtitle1 if same size)
    if (Layout::kFontBody1() == Layout::kFontSubtitle1()) {
        fonts_[8] = fonts_[6];  // Reuse Subtitle1 font
    } else {
        fonts_[8] = loadFont(io, kWeightRegular, Layout::kFontBody1() * scale, "Body1");
    }
    
    // Body2: Regular
    fonts_[9] = loadFont(io, kWeightRegular, Layout::kFontBody2() * scale, "Body2");
    
    // Button: Medium (shares with Subtitle2 if same size)
    if (Layout::kFontButton() == Layout::kFontSubtitle2()) {
        fonts_[10] = fonts_[7];  // Reuse Subtitle2 font
    } else {
        fonts_[10] = loadFont(io, kWeightMedium, Layout::kFontButton() * scale, "Button");
    }
    
    // Caption: Regular
    fonts_[11] = loadFont(io, kWeightRegular, Layout::kFontCaption() * scale, "Caption");
    
    // Overline: Regular
    fonts_[12] = loadFont(io, kWeightRegular, Layout::kFontOverline() * scale, "Overline");
    
    // ButtonSm: Medium
    fonts_[13] = loadFont(io, kWeightMedium, Layout::kFontButtonSm() * scale, "ButtonSm");
    
    // ButtonLg: Medium
    fonts_[14] = loadFont(io, kWeightMedium, Layout::kFontButtonLg() * scale, "ButtonLg");

    // Monospace (Ubuntu Mono) at caption size — used by the console so terminal text +
    // pretty-printed JSON columns align. Same size as Caption to keep line heights matched.
    mono_ = loadFont(io, kWeightMono, Layout::kFontCaption() * scale, "Mono");

    // --- Icon fonts at multiple sizes ---
    // These are standalone fonts (not merged) so we can PushFont(iconXxx) for icon rendering.
    iconFonts_[0] = loadIconFont(io, 14.0f * scale, "IconSmall");
    iconFonts_[1] = loadIconFont(io, 18.0f * scale, "IconMed");
    iconFonts_[2] = loadIconFont(io, 24.0f * scale, "IconLarge");
    iconFonts_[3] = loadIconFont(io, 40.0f * scale, "IconXL");

    // Load a one-glyph MDI subset for pickaxe. The glyph is remapped onto a
    // BMP private-use codepoint so it remains renderable with 16-bit ImWchar.
    pickaxeFonts_[0] = loadPickaxeFont(io, 14.0f * scale, "PickaxeSmall");
    pickaxeFonts_[1] = loadPickaxeFont(io, 18.0f * scale, "PickaxeMed");
    pickaxeFonts_[2] = loadPickaxeFont(io, 24.0f * scale, "PickaxeLarge");
    pickaxeFonts_[3] = loadPickaxeFont(io, 40.0f * scale, "PickaxeXL");

    // Verify all fonts loaded
    bool allLoaded = true;
    for (int i = 0; i < kNumStyles; ++i) {
        if (!fonts_[i]) {
            DEBUG_LOGF("Typography: Warning - Font for style %d not loaded\n", i);
            fonts_[i] = io.Fonts->Fonts.Size > 0 ? io.Fonts->Fonts[0] : nullptr;
            allLoaded = false;
        }
    }
    
    // Set Body1 as the default font so that bare ImGui::Text() calls
    // (status bar, dialogs, notifications, etc.) render at Body1 size
    // instead of H1 (the first font loaded).
    io.FontDefault = fonts_[static_cast<int>(TypeStyle::Body1)];
    
    loaded_ = true;
    Layout::setFontAtlasScale(Layout::userFontScale());
    DEBUG_LOGF("Typography: Loaded %d font styles (default=Body1, atlasScale=%.2f)\n",
               allLoaded ? kNumStyles : -1, Layout::fontAtlasScale());
    
    return allLoaded;
}

ImFont* Typography::loadFont(ImGuiIO& io, int weight, float size, const char* name)
{
    // Select font data based on weight
    const unsigned char* fontData = nullptr;
    unsigned int fontDataLen = 0;
    
    switch (weight) {
        case kWeightLight:
            fontData = g_ubuntu_light_data;
            fontDataLen = g_ubuntu_light_size;
            break;
        case kWeightMedium:
            fontData = g_ubuntu_medium_data;
            fontDataLen = g_ubuntu_medium_size;
            break;
        case kWeightMono:
            fontData = g_ubuntu_mono_data;
            fontDataLen = g_ubuntu_mono_size;
            break;
        case kWeightRegular:
        default:
            fontData = g_ubuntu_regular_data;
            fontDataLen = g_ubuntu_regular_size;
            break;
    }
    
    // ImGui needs to own a copy of the font data
    void* fontDataCopy = IM_ALLOC(fontDataLen);
    memcpy(fontDataCopy, fontData, fontDataLen);
    
    ImFontConfig cfg;
    cfg.FontDataOwnedByAtlas = true;  // ImGui will free this
    cfg.OversampleH = 2;
    cfg.OversampleV = 1;
    cfg.PixelSnapH = true;
    
    // Include default ASCII + Latin, Latin Extended (for Spanish/multilingual),
    // Cyrillic (for Russian), Greek, plus arrows (⇄ U+21C4),
    // math (≈ U+2248), general punctuation (— U+2014, … U+2026, etc.)
    static const ImWchar glyphRanges[] = {
        0x0020, 0x00FF,  // Basic Latin + Latin-1 Supplement
        0x0100, 0x024F,  // Latin Extended-A + Latin Extended-B (Spanish, etc.)
        0x0370, 0x03FF,  // Greek and Coptic
        0x0400, 0x04FF,  // Cyrillic (Russian, Ukrainian, etc.)
        0x0500, 0x052F,  // Cyrillic Supplement
        0x2000, 0x206F,  // General Punctuation (em dash, ellipsis, etc.)
        0x2190, 0x21FF,  // Arrows (includes ⇄ U+21C4, ↻ U+21BB)
        0x2200, 0x22FF,  // Mathematical Operators (includes ≈ U+2248)
        0x2600, 0x26FF,  // Miscellaneous Symbols (includes ⛏ U+26CF)
        0,
    };
    cfg.GlyphRanges = glyphRanges;
    
    // Create a unique name for this font variant
    const char* weightName = weight == kWeightLight  ? "Light"  :
                             weight == kWeightMedium ? "Medium" :
                             weight == kWeightMono   ? "Mono"   : "Regular";
    snprintf(cfg.Name, sizeof(cfg.Name), "Ubuntu %s %.0fpx", weightName, size);
    
    ImFont* font = io.Fonts->AddFontFromMemoryTTF(fontDataCopy, fontDataLen, size, &cfg);
    
    if (font) {
        DEBUG_LOGF("Typography: Loaded %s (%.0fpx) as '%s'\n", name, size, cfg.Name);
        
        // Merge CJK fallback glyphs (Chinese/Japanese/Korean) from subset font
        if (g_noto_cjk_subset_size > 0) {
            void* cjkCopy = IM_ALLOC(g_noto_cjk_subset_size);
            memcpy(cjkCopy, g_noto_cjk_subset_data, g_noto_cjk_subset_size);
            
            ImFontConfig cjkCfg;
            cjkCfg.FontDataOwnedByAtlas = true;
            cjkCfg.MergeMode = true;  // merge into the font we just loaded
            cjkCfg.OversampleH = 1;
            cjkCfg.OversampleV = 1;
            cjkCfg.PixelSnapH = true;
            cjkCfg.GlyphMinAdvanceX = 0;
            // CJK Unified Ideographs + Hiragana + Katakana + Hangul + fullwidth punctuation
            static const ImWchar cjkRanges[] = {
                0x2E80, 0x2FDF,  // CJK Radicals
                0x3000, 0x30FF,  // CJK Symbols, Hiragana, Katakana
                0x3100, 0x312F,  // Bopomofo
                0x31F0, 0x31FF,  // Katakana Extensions
                0x3400, 0x4DBF,  // CJK Extension A
                0x4E00, 0x9FFF,  // CJK Unified Ideographs
                0xAC00, 0xD7AF,  // Hangul Syllables
                0xFF00, 0xFFEF,  // Fullwidth Forms
                0,
            };
            cjkCfg.GlyphRanges = cjkRanges;
            snprintf(cjkCfg.Name, sizeof(cjkCfg.Name), "NotoSansCJK %.0fpx (merge)", size);
            
            ImFont* mergeResult = io.Fonts->AddFontFromMemoryTTF(cjkCopy, g_noto_cjk_subset_size, size, &cjkCfg);
            if (mergeResult) {
                DEBUG_LOGF("Typography: Merged CJK (%u bytes) into %s OK\n",
                           g_noto_cjk_subset_size, name);
            } else {
                DEBUG_LOGF("Typography: WARNING — CJK merge FAILED for %s (size=%u)\n",
                           name, g_noto_cjk_subset_size);
            }
        }

        // Merge monochrome emoji for chat + user text (Q12). Only into the small text fonts — baking
        // ~1400 emoji at heading sizes would bloat the atlas for glyphs no heading needs. ImGui does no
        // shaping, so single-codepoint emoji render but ZWJ sequences / flags won't compose. The
        // >0xFFFF ranges require IMGUI_USE_WCHAR32 (imconfig.h).
        static const char* const kEmojiFonts[] = {
            "Body1", "Body2", "Subtitle1", "Subtitle2", "Caption", "Overline", "Button", "ButtonSm"
        };
        bool wantEmoji = false;
        for (const char* n : kEmojiFonts) if (strcmp(name, n) == 0) { wantEmoji = true; break; }

        // Emoji blob: the COLR/CPAL color font (FreeType-rendered) when color emoji is enabled and this
        // is a FreeType build, else the monochrome subset (default / non-FreeType path).
        const unsigned char* emojiData = g_noto_emoji_subset_data;
        unsigned int         emojiSize = g_noto_emoji_subset_size;
        bool                 colorGlyphs = false;
#ifdef DRAGONX_HAVE_FREETYPE
        if (color_emoji_ && g_twemoji_color_size > 0) {
            emojiData   = g_twemoji_color_data;
            emojiSize   = g_twemoji_color_size;
            colorGlyphs = true;
        }
#endif
        if (wantEmoji && emojiSize > 0) {
            void* emojiCopy = IM_ALLOC(emojiSize);
            memcpy(emojiCopy, emojiData, emojiSize);

            ImFontConfig emojiCfg;
            emojiCfg.FontDataOwnedByAtlas = true;
            emojiCfg.MergeMode = true;  // merge into the text font just loaded
            emojiCfg.OversampleH = 1;
            emojiCfg.OversampleV = 1;
            emojiCfg.PixelSnapH = true;
            emojiCfg.GlyphMinAdvanceX = 0;
#ifdef DRAGONX_HAVE_FREETYPE
            if (colorGlyphs) emojiCfg.FontLoaderFlags |= ImGuiFreeTypeLoaderFlags_LoadColor;  // render COLR in color
#endif
            // The base Ubuntu font already owns U+2600–26FF etc.; MergeMode keeps the first-loaded glyph,
            // so its text-style symbols win and only the codepoints it lacks fall through to emoji.
            static const ImWchar emojiRanges[] = {
                0x2600,  0x27BF,   // Misc Symbols + Dingbats
                0x2B00,  0x2BFF,   // stars (⭐) + arrows
                0x1F000, 0x1FAFF,  // emoji planes (emoticons, pictographs, transport, supplement, extended)
                0,
            };
            emojiCfg.GlyphRanges = emojiRanges;
            snprintf(emojiCfg.Name, sizeof(emojiCfg.Name), "%s %.0fpx (merge)",
                     colorGlyphs ? "Twemoji" : "NotoEmoji", size);

            ImFont* emojiMerge = io.Fonts->AddFontFromMemoryTTF(emojiCopy, emojiSize, size, &emojiCfg);
            if (emojiMerge) {
                DEBUG_LOGF("Typography: Merged %s emoji (%u bytes) into %s OK\n",
                           colorGlyphs ? "color" : "mono", emojiSize, name);
            } else {
                DEBUG_LOGF("Typography: WARNING — emoji merge FAILED for %s (size=%u)\n", name, size);
            }
        }
    } else {
        DEBUG_LOGF("Typography: Failed to load %s\n", name);
        IM_FREE(fontDataCopy);
    }

    return font;
}

ImFont* Typography::loadIconFont(ImGuiIO& io, float size, const char* name)
{
    // ImGui needs to own a copy of the font data
    void* fontDataCopy = IM_ALLOC(g_material_icons_size);
    memcpy(fontDataCopy, g_material_icons_data, g_material_icons_size);

    ImFontConfig cfg;
    cfg.FontDataOwnedByAtlas = true;
    cfg.OversampleH = 2;
    cfg.OversampleV = 1;
    cfg.PixelSnapH = true;
    cfg.MergeMode = false;  // standalone icon font
    cfg.GlyphMinAdvanceX = size; // monospace icons

    static const ImWchar iconRanges[] = { ICON_MIN_MD, ICON_MAX_16_MD, 0 };
    cfg.GlyphRanges = iconRanges;

    snprintf(cfg.Name, sizeof(cfg.Name), "MDIcons %.0fpx", size);

    ImFont* font = io.Fonts->AddFontFromMemoryTTF(fontDataCopy, g_material_icons_size, size, &cfg);
    if (font) {
        DEBUG_LOGF("Typography: Loaded icon font %s (%.0fpx)\n", name, size);
    } else {
        DEBUG_LOGF("Typography: Failed to load icon font %s\n", name);
        IM_FREE(fontDataCopy);
    }
    return font;
}

ImFont* Typography::loadPickaxeFont(ImGuiIO& io, float size, const char* name)
{
    if (g_mdi_pickaxe_subset_size == 0) {
        DEBUG_LOGF("Typography: Pickaxe subset font is empty\n");
        return nullptr;
    }

    void* fontDataCopy = IM_ALLOC(g_mdi_pickaxe_subset_size);
    memcpy(fontDataCopy, g_mdi_pickaxe_subset_data, g_mdi_pickaxe_subset_size);

    ImFontConfig cfg;
    cfg.FontDataOwnedByAtlas = true;
    cfg.OversampleH = 2;
    cfg.OversampleV = 1;
    cfg.PixelSnapH = true;
    cfg.MergeMode = false;
    cfg.GlyphMinAdvanceX = size;

    static const ImWchar pickaxeRange[] = {
        Typography::kPickaxeCodepoint,
        Typography::kPickaxeCodepoint,
        0,
    };
    cfg.GlyphRanges = pickaxeRange;

    snprintf(cfg.Name, sizeof(cfg.Name), "MDIPickaxe %.0fpx", size);

    ImFont* font = io.Fonts->AddFontFromMemoryTTF(fontDataCopy, g_mdi_pickaxe_subset_size, size, &cfg);
    if (font) {
        DEBUG_LOGF("Typography: Loaded pickaxe font %s (%.0fpx)\n", name, size);
    } else {
        DEBUG_LOGF("Typography: Failed to load pickaxe font %s\n", name);
        IM_FREE(fontDataCopy);
    }
    return font;
}

ImFont* Typography::pickaxeFontForSize(float size) const
{
    if (size <= 15.0f) return pickaxeSmall();
    if (size <= 20.0f) return pickaxeMed();
    if (size <= 32.0f) return pickaxeLarge();
    return pickaxeXL();
}

ImFont* Typography::getFont(TypeStyle style) const
{
    int index = static_cast<int>(style);
    if (index >= 0 && index < kNumStyles && fonts_[index]) {
        return fonts_[index];
    }
    // Return default font if not found
    return ImGui::GetIO().Fonts->Fonts.Size > 0 ? 
           ImGui::GetIO().Fonts->Fonts[0] : nullptr;
}

const TypeSpec& Typography::getSpec(TypeStyle style) const
{
    const TypeSpec* specs = getTypeSpecs();
    int index = static_cast<int>(style);
    if (index >= 0 && index < kNumStyles) {
        return specs[index];
    }
    // Return Body1 as default
    return specs[static_cast<int>(TypeStyle::Body1)];
}

void Typography::pushFont(TypeStyle style) const
{
    ImGui::PushFont(getFont(style));
}

void Typography::popFont() const
{
    ImGui::PopFont();
}

// ============================================================================
// Text Rendering Helpers
// ============================================================================

void Typography::text(TypeStyle style, const char* text) const
{
    const TypeSpec& spec = getSpec(style);
    pushFont(style);
    
    if (spec.uppercase) {
        // Convert to uppercase
        std::string upper;
        upper.reserve(strlen(text));
        for (const char* p = text; *p; ++p) {
            upper += static_cast<char>(std::toupper(static_cast<unsigned char>(*p)));
        }
        ImGui::TextUnformatted(upper.c_str());
    } else {
        ImGui::TextUnformatted(text);
    }
    
    popFont();
}

void Typography::textWrapped(TypeStyle style, const char* text) const
{
    const TypeSpec& spec = getSpec(style);
    pushFont(style);
    
    if (spec.uppercase) {
        std::string upper;
        upper.reserve(strlen(text));
        for (const char* p = text; *p; ++p) {
            upper += static_cast<char>(std::toupper(static_cast<unsigned char>(*p)));
        }
        ImGui::TextWrapped("%s", upper.c_str());
    } else {
        ImGui::TextWrapped("%s", text);
    }
    
    popFont();
}

void Typography::textColored(TypeStyle style, ImU32 color, const char* text) const
{
    const TypeSpec& spec = getSpec(style);
    pushFont(style);
    
    ImVec4 colorVec = ImGui::ColorConvertU32ToFloat4(color);
    
    if (spec.uppercase) {
        std::string upper;
        upper.reserve(strlen(text));
        for (const char* p = text; *p; ++p) {
            upper += static_cast<char>(std::toupper(static_cast<unsigned char>(*p)));
        }
        ImGui::TextColored(colorVec, "%s", upper.c_str());
    } else {
        ImGui::TextColored(colorVec, "%s", text);
    }
    
    popFont();
}

} // namespace material
} // namespace ui
} // namespace dragonx
