/*
Crafter®.Graphics
Copyright (C) 2026 Catcrafts®
Catcrafts.net

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
*/

module;
#include "../lib/stb_truetype.h"
export module Crafter.Graphics:Font;
import std;

namespace Crafter {
    // Decode the UTF-8 codepoint at `text[i]` and advance `i` past it.
    // Returns 0 once `i` reaches the end. Malformed sequences yield U+FFFD
    // and the index is moved past one byte to keep iteration finite.
    export inline std::uint32_t DecodeUtf8(std::string_view text, std::size_t& i) {
        if (i >= text.size()) return 0;
        std::uint8_t b0 = static_cast<std::uint8_t>(text[i]);

        if (b0 < 0x80) { ++i; return b0; }

        int extra;
        std::uint32_t cp;
        if      ((b0 & 0xE0) == 0xC0) { extra = 1; cp = b0 & 0x1F; }
        else if ((b0 & 0xF0) == 0xE0) { extra = 2; cp = b0 & 0x0F; }
        else if ((b0 & 0xF8) == 0xF0) { extra = 3; cp = b0 & 0x07; }
        else { ++i; return 0xFFFD; }

        ++i;
        for (int k = 0; k < extra; ++k) {
            if (i >= text.size()) return 0xFFFD;
            std::uint8_t b = static_cast<std::uint8_t>(text[i]);
            if ((b & 0xC0) != 0x80) return 0xFFFD;
            cp = (cp << 6) | (b & 0x3Fu);
            ++i;
        }
        return cp;
    }

    export class Font {
    public:
        std::vector<unsigned char> fontBuffer;
        std::int_fast32_t ascent;
        std::int_fast32_t descent;
        std::int_fast32_t lineGap;
        stbtt_fontinfo font;
        Font(const std::filesystem::path& font);
        std::uint32_t GetLineWidth(const std::string_view text, float size);
        // Map a target advance (px from the line start) to the nearest UTF-8
        // codepoint boundary, returning its BYTE offset into `text`. A single
        // left-to-right pass accumulates per-glyph advances (O(n) metric
        // lookups), instead of re-walking the prefix for every boundary as a
        // naive caller would. Cumulative advance is monotonic (advances are
        // non-negative), so the scan stops at the first boundary past the
        // closest one. Returned offsets land on codepoint boundaries, which are
        // exactly the valid positions for a byte-based text cursor.
        std::size_t NearestCursorByte(std::string_view text, float size, float target);
        float LineHeight(float size);
        float AscentPx(float size);
        float ScaleForSize(float size);

        // Horizontal advance for `cp` in unscaled font units, cached per Font.
        // Stored in font units (not pixels) so it can be rescaled for any
        // `size`; Glyph::advance in the FontAtlas is baked at kBaseSize and is
        // wrong at other sizes, so it cannot be reused here. ASCII lands in a
        // flat array (the common path for caret hit-testing, which rescans the
        // whole field per character), everything else in the map.
        std::int32_t AdvanceUnits(std::uint32_t cp);

    private:
        std::array<std::int32_t, 128> asciiAdvance_;            // -1 = uncached
        std::unordered_map<std::uint32_t, std::int32_t> advanceUnits_;
    };
}
