#pragma once

#if defined( __EMSCRIPTEN__ ) || defined( __ANDROID__ )
#include <GLES3/gl3.h>
#else
#include <glad/glad.h>
#endif

#include <entt/entt.hpp>

#include "crArray.h"
#include "crStruct.h"

struct TTF_Font;

// SDL_ttf renders each glyph into a full advance x fontHeight cell (bearing + baseline baked in), so layout uses only sizePx + advance.
struct crFontGlyph
{
    uint8_t page    = 0;
    float2  uvMin   = {};   // uvMin == uvMax => no bitmap; advance still applies
    float2  uvMax   = {};
    float2  sizePx  = {};   // the rendered cell, not the tight glyph
    float   advance = 0.0f;
};

struct crFontMetrics
{
    int32_t lineHeight = 0;   // line-to-line advance
    int32_t ascent     = 0;   // baseline offset from the cell top
};

class crFontAtlas
{
public:
    static constexpr int32_t TEXT_ATLAS_PAGE_SIZE = 2048;
    static constexpr int32_t GLYPH_PADDING = 1;   // 1px gutter; no mips so no bleed spread is needed

private:
    struct Page
    {
        GLuint  texture;
        int32_t shelfX;
        int32_t shelfY;
        int32_t shelfHeight;
    };

    struct PackResult
    {
        bool    ok;
        uint8_t page;
        int32_t x;
        int32_t y;
    };

    // font nullptr = open failed; the entry still caches so the error logs once, not per glyph
    struct FontEntry
    {
        char          name[ 64 ];
        int32_t       px;       // logical px at the 720p reference height; actual raster = px * resScale
        TTF_Font*     font;
        crFontMetrics metrics;
    };

    float _resScale = 1.0f;   // viewportH / LOGICAL_HEIGHT — fonts are re-rastered at this scale

    crArray<FontEntry> _fonts;

    crArray<Page>                          _pages;
    entt::dense_map<uint64_t, crFontGlyph> _glyphs;

public:
    bool Init();
    void Cleanup();

    int32_t LoadFont( const char* baseName, int32_t px );   // crassets/fonts/<base>.ttf, cached by (name, px); the handle stays valid for the app lifetime

    void Rebuild( float resScale );   // resolution changed — re-open fonts at the new scale and re-raster (skips if unchanged)

    crFontGlyph   Glyph( uint32_t codepoint, int32_t font );   // caches on first request
    crFontMetrics Metrics( int32_t font ) const
    {
        return _fonts.At( font ).metrics;
    }

    GLuint PageTexture( int32_t page ) const
    {
        return _pages.At( page ).texture;
    }

private:
    void OpenFont( int32_t font );          // (re)opens one registry entry at px * _resScale, refreshes its metrics
    void ReleaseFontsAndPages();            // closes fonts, deletes page textures, clears the glyph cache

    void        PreloadGlyphs( int32_t font );   // printable ASCII
    crFontGlyph RasterizeGlyph( uint32_t codepoint, int32_t fontId );
    PackResult  PackGlyph( int32_t w, int32_t h );
    void        OpenPage();

    static uint64_t GlyphKey( uint32_t codepoint, int32_t font );
};
