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

#pragma once

#include "imgui.h"
#include <string>

namespace dragonx {
namespace ui {
namespace material {

// ============================================================================
// Material Design Type Scale
// ============================================================================
// Based on https://m2.material.io/design/typography/the-type-system.html
//
// The type scale is a combination of 13 styles that are supported by the
// type system. It contains reusable categories of text, each with an
// intended application and meaning.

/**
 * @brief Material Design typography style identifiers
 */
enum class TypeStyle {
    H1,         // 96sp Light  - Large display headers
    H2,         // 60sp Light  - Display headers
    H3,         // 48sp Regular - Section titles
    H4,         // 34sp Regular - Section titles
    H5,         // 24sp Regular - Card titles, dialog titles
    H6,         // 20sp Medium  - Section headers, subtitles
    Subtitle1,  // 16sp Regular - Secondary information
    Subtitle2,  // 14sp Medium  - Secondary information
    Body1,      // 16sp Regular - Primary body text
    Body2,      // 14sp Regular - Secondary body text
    Button,     // 14sp Medium  - Button labels (uppercase)
    Caption,    // 12sp Regular - Timestamps, labels
    Overline,   // 10sp Regular - Overline labels (uppercase)
    ButtonSm,   // Small button labels
    ButtonLg    // Large button labels
};

/**
 * @brief Typography specification for a type style
 */
struct TypeSpec {
    float size;           // Font size in sp (scaled pixels)
    float letterSpacing;  // Letter spacing in px (applied after scaling)
    float lineHeight;     // Line height multiplier
    bool uppercase;       // Whether text should be uppercase
    
    // Font weight is handled by which font is used (Light/Regular/Medium)
};

/**
 * @brief Typography system managing Material Design fonts
 * 
 * Loads Ubuntu fonts in Light, Regular, and Medium weights at multiple
 * sizes to implement the Material Design type scale.
 * 
 * Usage:
 *   // Initialize during startup (after ImGui context)
 *   dragonx::ui::material::Typography::instance().load(io);
 *   
 *   // Use fonts throughout the application
 *   ImGui::PushFont(Typography::instance().getFont(TypeStyle::H5));
 *   ImGui::Text("Card Title");
 *   ImGui::PopFont();
 *   
 *   // Or use convenience functions
 *   Typography::instance().pushFont(TypeStyle::Body1);
 *   ImGui::TextWrapped("Body text here...");
 *   Typography::instance().popFont();
 */
class Typography {
public:
    /**
     * @brief Get the singleton instance
     */
    static Typography& instance();
    
    /**
     * @brief Load all fonts for the type scale
     * 
     * Must be called after ImGui context is created but before first frame.
     * 
     * @param io ImGui IO reference
     * @param dpiScale DPI scale factor (default 1.0)
     * @return true if fonts loaded successfully
     */
    bool load(ImGuiIO& io, float dpiScale = 1.0f);
    
    /**
     * @brief Reload fonts at a new DPI scale
     * 
     * Call when the display scale changes (e.g., window moved to a different
     * DPI monitor). Clears the existing font atlas and reloads all fonts.
     * 
     * @param io ImGui IO reference
     * @param dpiScale New DPI scale factor
     * @return true if fonts reloaded successfully
     */
    bool reload(ImGuiIO& io, float dpiScale);
    
    /**
     * @brief Check if typography system is loaded
     */
    bool isLoaded() const { return loaded_; }
    
    /**
     * @brief Get the current DPI scale
     */
    float getDpiScale() const { return dpiScale_; }

    /**
     * @brief Select color vs monochrome emoji for the next (re)load. Color needs a FreeType-enabled
     *        build (DRAGONX_HAVE_FREETYPE); otherwise this is inert and monochrome is always used.
     *        Set before load()/reload() (App does this from the chat_emoji_color setting).
     */
    void setColorEmoji(bool enabled) { color_emoji_ = enabled; }
    bool colorEmoji() const { return color_emoji_; }
    
    /**
     * @brief Get font for a type style
     * 
     * @param style The Material Design type style
     * @return ImFont pointer (never null, returns default if not loaded)
     */
    ImFont* getFont(TypeStyle style) const;
    
    /**
     * @brief Get the spec for a type style
     * 
     * @param style The Material Design type style
     * @return TypeSpec with size, spacing, and line height
     */
    const TypeSpec& getSpec(TypeStyle style) const;
    
    /**
     * @brief Push font for a type style
     * 
     * Convenience wrapper around ImGui::PushFont(getFont(style))
     */
    void pushFont(TypeStyle style) const;
    
    /**
     * @brief Pop the current font
     * 
     * Convenience wrapper around ImGui::PopFont()
     */
    void popFont() const;
    
    // ========================================================================
    // Font Accessors (for common cases)
    // ========================================================================
    
    // Headers
    ImFont* h1() const { return getFont(TypeStyle::H1); }
    ImFont* h2() const { return getFont(TypeStyle::H2); }
    ImFont* h3() const { return getFont(TypeStyle::H3); }
    ImFont* h4() const { return getFont(TypeStyle::H4); }
    ImFont* h5() const { return getFont(TypeStyle::H5); }
    ImFont* h6() const { return getFont(TypeStyle::H6); }
    
    // Body text
    ImFont* subtitle1() const { return getFont(TypeStyle::Subtitle1); }
    ImFont* subtitle2() const { return getFont(TypeStyle::Subtitle2); }
    ImFont* body1() const { return getFont(TypeStyle::Body1); }
    ImFont* body2() const { return getFont(TypeStyle::Body2); }
    
    // UI elements
    ImFont* button() const { return getFont(TypeStyle::Button); }
    ImFont* buttonSm() const { return getFont(TypeStyle::ButtonSm); }
    ImFont* buttonLg() const { return getFont(TypeStyle::ButtonLg); }
    ImFont* caption() const { return getFont(TypeStyle::Caption); }
    ImFont* overline() const { return getFont(TypeStyle::Overline); }

    // Monospace (Ubuntu Mono) at caption size — for the console, so JSON/columns align
    // like a real terminal. Falls back to the proportional caption font if not loaded.
    ImFont* mono() const { return mono_ ? mono_ : caption(); }

    // Icon fonts — Material Design Icons merged at specific pixel sizes
    // Use these when you need to render an icon at a specific size via AddText/ImGui::Text.
    // Common sizes: iconSmall (14px), iconMed (18px), iconLarge (24px).
    ImFont* iconSmall() const { return iconFonts_[0] ? iconFonts_[0] : getFont(TypeStyle::Body2); }
    ImFont* iconMed()   const { return iconFonts_[1] ? iconFonts_[1] : getFont(TypeStyle::Body1); }
    ImFont* iconLarge() const { return iconFonts_[2] ? iconFonts_[2] : getFont(TypeStyle::H5); }
    ImFont* iconXL()    const { return iconFonts_[3] ? iconFonts_[3] : getFont(TypeStyle::H3); }
    ImFont* pickaxeSmall() const { return pickaxeFonts_[0] ? pickaxeFonts_[0] : iconSmall(); }
    ImFont* pickaxeMed()   const { return pickaxeFonts_[1] ? pickaxeFonts_[1] : iconMed(); }
    ImFont* pickaxeLarge() const { return pickaxeFonts_[2] ? pickaxeFonts_[2] : iconLarge(); }
    ImFont* pickaxeXL()    const { return pickaxeFonts_[3] ? pickaxeFonts_[3] : iconXL(); }

    ImFont* pickaxeFontForSize(float size) const;

    static constexpr ImWchar kPickaxeCodepoint = 0xE001;

    /**
     * @brief Resolve a font name string to ImFont*
     * @param name Font name like "button", "button-sm", "button-lg", "h4", "body1"
     * @return ImFont* pointer, or nullptr if name is empty/unknown
     */
    ImFont* resolveByName(const std::string& name) const {
        if (name.empty()) return nullptr;
        if (name == "h1")         return h1();
        if (name == "h2")         return h2();
        if (name == "h3")         return h3();
        if (name == "h4")         return h4();
        if (name == "h5")         return h5();
        if (name == "h6")         return h6();
        if (name == "subtitle1")  return subtitle1();
        if (name == "subtitle2")  return subtitle2();
        if (name == "body1")      return body1();
        if (name == "body2")      return body2();
        if (name == "button")     return button();
        if (name == "button-sm")  return buttonSm();
        if (name == "button-lg")  return buttonLg();
        if (name == "caption")    return caption();
        if (name == "overline")   return overline();
        return nullptr;
    }
    
    // ========================================================================
    // Text Rendering Helpers
    // ========================================================================
    
    /**
     * @brief Render text with a specific type style
     * 
     * Handles font push/pop and optional uppercase transformation.
     * 
     * @param style The type style to use
     * @param text The text to render
     */
    void text(TypeStyle style, const char* text) const;
    
    /**
     * @brief Render wrapped text with a specific type style
     * 
     * @param style The type style to use
     * @param text The text to render
     */
    void textWrapped(TypeStyle style, const char* text) const;
    
    /**
     * @brief Render colored text with a specific type style
     * 
     * @param style The type style to use
     * @param color Text color
     * @param text The text to render
     */
    void textColored(TypeStyle style, ImU32 color, const char* text) const;
    
private:
    Typography() = default;
    ~Typography() = default;
    Typography(const Typography&) = delete;
    Typography& operator=(const Typography&) = delete;
    
    // Load fonts at a specific size with a specific weight
    ImFont* loadFont(ImGuiIO& io, int weight, float size, const char* name);
    
    // Font weight constants
    static constexpr int kWeightLight = 300;
    static constexpr int kWeightRegular = 400;
    static constexpr int kWeightMedium = 500;
    static constexpr int kWeightMono = 401;   // selects Ubuntu Mono (monospace, regular)
    
    bool loaded_ = false;
    float dpiScale_ = 1.0f;
    bool color_emoji_ = false;   // when true + FreeType present, merge the COLR color-emoji font

    // Fonts for each type style
    ImFont* fonts_[15] = {};

    // Monospace font (Ubuntu Mono) at caption size — console/terminal text.
    ImFont* mono_ = nullptr;
    
    // Icon fonts at different sizes: [0]=small(14), [1]=med(18), [2]=large(24), [3]=xl(40)
    ImFont* iconFonts_[4] = {};
    ImFont* pickaxeFonts_[4] = {};
    static constexpr int kNumIconSizes = 4;
    
    // Load an icon-only font at a specific pixel size
    ImFont* loadIconFont(ImGuiIO& io, float size, const char* name);
    ImFont* loadPickaxeFont(ImGuiIO& io, float size, const char* name);
    
    // Type specifications
    static const TypeSpec* getTypeSpecs();
    static constexpr int kNumStyles = 15;
};

// ============================================================================
// Convenience Macros (Optional)
// ============================================================================

// Scoped font push/pop using RAII
class ScopedFont {
public:
    explicit ScopedFont(TypeStyle style) {
        Typography::instance().pushFont(style);
    }
    ~ScopedFont() {
        Typography::instance().popFont();
    }
};

// Usage: MATERIAL_FONT(H5) { ImGui::Text("Title"); }
#define MATERIAL_FONT(style) \
    if (dragonx::ui::material::ScopedFont _font{dragonx::ui::material::TypeStyle::style}; true)

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