//
// Created by vfs on 1/28/26.
//

#include "font.h"

#include <print>
#include <fstream>
#include <rlgl.h>
#include <stdexcept>
#include <stb_rect_pack.h>
#include <tracy/Tracy.hpp>
#include <raymath.h>

FT_Library ft_lib;
Shader shader;

extern Vector2 DPI;

const int32_t ATLAS_SIZE = 4096;

MyFont::MyFont(const std::string& filename, int size) {

    // Load file data
    std::ifstream file(filename, std::ios::binary | std::ios::ate);
    if (!file.is_open()) std::println("Couldn't open the file! {}", filename);

    std::streamsize file_size = file.tellg();
    std::vector<uint8_t> file_data(file_size);
    file.seekg(0, std::ios::beg);
    file.read(reinterpret_cast<char*>(file_data.data()), file_size);

    kb_file_data = file_data;

    face = {};
    if (FT_New_Memory_Face(ft_lib, file_data.data(), file_data.size(), 0, &face)) {
        throw std::runtime_error("Failed to create FreeType face");
    }

    std::println("DPI: {}, {}", DPI.x, DPI.y);

    FT_Set_Char_Size(face, 0, size * 64, 0, 96.0 * DPI.y);
    FT_Select_Charmap(face, FT_ENCODING_UNICODE);

    std::vector<stbrp_rect> rects;
    rects.reserve(face->num_glyphs);

    for (int i = 0; i < face->num_glyphs; i++) {
        FT_Load_Glyph(face, i, FT_LOAD_DEFAULT);
        stbrp_rect r = {};
        r.id = i;
        r.w = (stbrp_coord)std::max(1, (int)face->glyph->bitmap.width);
        r.h = (stbrp_coord)std::max(1, (int)face->glyph->bitmap.rows);
        rects.push_back(r);
    }

    stbrp_context context;
    std::vector<stbrp_node> nodes(ATLAS_SIZE);
    stbrp_init_target(&context, ATLAS_SIZE, ATLAS_SIZE, nodes.data(), nodes.size());

    stbrp_pack_rects(&context, rects.data(), rects.size());

    std::vector<uint8_t> atlas_data(ATLAS_SIZE * ATLAS_SIZE, 0);

    for (const auto& rect : rects) {
        if (!rect.was_packed) continue;

        FT_Load_Glyph(face, rect.id, FT_LOAD_RENDER);
        FT_Bitmap& bmp = face->glyph->bitmap;

        for (uint32_t row = 0; row < bmp.rows; ++row) {
            for (uint32_t col = 0; col < bmp.width; ++col) {
                int dst_idx = (rect.y + row) * ATLAS_SIZE + (rect.x + col);
                atlas_data[dst_idx] = bmp.buffer[row * bmp.pitch + col];
            }
        }

        Glyph g = {};
        g.index = rect.id;
        g.width = bmp.width;
        g.height = bmp.rows;
        g.dpi_width = g.width * DPI.x;
        g.dpi_height = (g.height) * DPI.y;
        g.bearing_x = face->glyph->bitmap_left;
        g.bearing_y = face->glyph->bitmap_top;
        g.dpi_bearing_x = g.bearing_x * DPI.x;
        g.dpi_bearing_y = g.bearing_y * DPI.y;
        g.descent = g.height - g.bearing_y;
        g.ascent = g.height - g.descent;
        g.dpi_descent = g.dpi_height - g.dpi_bearing_y;
        g.dpi_ascent = g.dpi_height - g.dpi_descent;

        g.x = (int16_t)rect.x;
        g.y = (int16_t)(rect.y + g.height);

        float fx = (float)g.x;
        float fy = (float)g.y;
        float fw = (float)g.width;
        float fh = (float)g.height;
        float fs = (float)ATLAS_SIZE;

        g.st0 = { (fx + 0.5f) / fs, (fy - fh + 0.5f) / fs };
        g.st1 = { (fx + fw - 0.5f) / fs, (fy - 0.5f) / fs };

        glyphs[g.index] = g;
    }

    kbts = kbts_FontFromMemory(kb_file_data.data(), kb_file_data.size(), 0, nullptr, nullptr);

    if (kbts_FontIsValid(&kbts) == 0) {
        std::println("[Error] [kb_text_shape] Failed to load the font.");
    }

    Image img = {
        .data = atlas_data.data(),
        .width = ATLAS_SIZE,
        .height = ATLAS_SIZE,
        .mipmaps = 1,
        .format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE
    };

    texture = LoadTextureFromImage(img);
}

void MyFont::deinit() {
    kbts_FreeFont(&kbts);
    if (face) FT_Done_Face(face);
    UnloadTexture(texture);
    glyphs.clear();
    kb_file_data.clear();
}

void MyFont::render_text(const std::string_view text, const Vector2 pos, const Color colour)
{
    if (text.empty())
        return;

    // if (nice_background) {
    //     self.render_text(
    //         text,
    //         pos.add(.{.x = 3.0, .y = -3.0}),
    //         window_space,
    //         rl.Color{ .r = 0, .g = 0, .b = 0, .a = 255 },
    //         rl.Color{ .r = 0, .g = 0, .b = 0, .a = 0 },
    //         false,
    //         false
    //     );
    // }

    ZoneScoped

    Vector2 render_pos = pos;

    BeginShaderMode(shader);
    rlSetTexture(texture.id);
    rlBegin(RL_QUADS);

    const float font_ascent = face->size->metrics.ascender >> 6;
    const float dpi_font_ascent = font_ascent * DPI.y;

    render_pos.y += font_ascent;

    kbts_shape_context *Context = kbts_CreateShapeContext(nullptr, nullptr);
    const kbts_font *kb_font = kbts_ShapePushFont(Context, &kbts);
    if (kb_font == nullptr) {
        std::println("Could not open font!");
        return;
    }

    kbts_ShapeBegin(Context, KBTS_DIRECTION_DONT_KNOW, KBTS_LANGUAGE_DONT_KNOW);
    kbts_ShapeUtf8(Context, text.data(), text.size(), KBTS_USER_ID_GENERATION_MODE_CODEPOINT_INDEX);
    kbts_ShapeEnd(Context);

    float cursor_x = 0;
    float cursor_y = 0;

    kbts_run Run;
    while (my_kbts_ShapeRun(Context, &Run) != 0) {
        ZoneScopedN("shape run")
        if ((Run.Flags & KBTS_BREAK_FLAG_LINE_HARD) != 0) {
            const float font_linegap = face->size->metrics.height >> 6;
            const float dpi_font_linegap = font_linegap * DPI.y;

            cursor_y += font_linegap;
            cursor_x = 0;
        }

        kbts_glyph *RunGlyph;
        while (kbts_GlyphIteratorNext(&Run.Glyphs, &RunGlyph) != 0) {
            ZoneScopedN("glyph iterator")
            const int32_t CodepointIndex = RunGlyph->UserIdOrCodepointIndex;
            kbts_shape_codepoint ShapeCodepoint;
            if (kbts_ShapeGetShapeCodepoint(Context, CodepointIndex, &ShapeCodepoint) == 0) {
                std::println("aah it's wrong!! idk how to handle the error rn!!");
            }

            const float advance_x = FT_MulFix(RunGlyph->AdvanceX, face->size->metrics.x_scale) >> 6;
            const float advance_y = FT_MulFix(RunGlyph->AdvanceY, face->size->metrics.y_scale) >> 6;
            const float dpi_advance_x = advance_x * DPI.x;
            const float dpi_advance_y = advance_y * DPI.y;

            const float offset_x = FT_MulFix(RunGlyph->OffsetX, face->size->metrics.x_scale) >> 6;
            const float offset_y = FT_MulFix(RunGlyph->OffsetY, face->size->metrics.y_scale) >> 6;
            const float dpi_offset_x = offset_x * DPI.x;
            const float dpi_offset_y = offset_y * DPI.y;

            if (auto it = glyphs.find(RunGlyph->Id); it != glyphs.end()) {
                auto &glyph = it->second;

                const Vector2 v0 = Vector2Add(render_pos, { .x = cursor_x + glyph.bearing_x + offset_x,
                                                 .y = cursor_y - glyph.bearing_y - offset_y });

                const Vector2 v1 = Vector2Add(v0, Vector2{ .x = (float) glyph.width, .y = (float) glyph.height });
                const Vector4 p0 = { .x = v0.x, .y = v0.y, .z = 0.0, .w = 1.0 };
                const Vector4 p1 = { .x = v1.x, .y = v1.y, .z = 0.0, .w = 1.0 };

                cursor_x += advance_x;
                cursor_y += advance_y;

                const Vector2 st0 = glyph.st0;
                const Vector2 st1 = glyph.st1;

                rlColor4ub(colour.r, colour.g, colour.b, colour.a);
                rlNormal3f(0.0, 0.0, 1.0);

                rlTexCoord2f(st0.x, st0.y); rlVertex2f(p0.x, p0.y);
                rlTexCoord2f(st0.x, st1.y); rlVertex2f(p0.x, p1.y);
                rlTexCoord2f(st1.x, st1.y); rlVertex2f(p1.x, p1.y);
                rlTexCoord2f(st1.x, st0.y); rlVertex2f(p1.x, p0.y);

            } else {
                std::println("kb_text_shape found the glyph, but we didn't load it from the font. index {}",
                    RunGlyph->Id);
                std::println("advance_x {}", advance_x);
                cursor_x += dpi_advance_x;
                cursor_y += dpi_advance_y;
            }
        }
    }

    rlEnd();
    rlSetTexture(0);
    EndShaderMode();

    kbts_DestroyShapeContext(Context);
}

struct size_ret {
    Vector2 size;
    float max_descent;
    float ascent;
};

MyFont::size_ret MyFont::size_row(std::string_view str, int32_t n, float max_width) {
    const auto Context = kbts_CreateShapeContext(nullptr, nullptr);
    const auto kb_font = kbts_ShapePushFont(Context, &kbts);
    if (kb_font == nullptr) {
        std::println("Could not open font!");
        return { Vector2{}, 0, 0 };
    }

    kbts_ShapeBegin(Context, KBTS_DIRECTION_DONT_KNOW, KBTS_LANGUAGE_DONT_KNOW);
    kbts_ShapeUtf8(Context, str.data(), str.size(), KBTS_USER_ID_GENERATION_MODE_CODEPOINT_INDEX);
    kbts_ShapeEnd(Context);

    Vector2 size = Vector2{};
    float max_descent = 0;

    kbts_run Run{};
    while(kbts_ShapeRun(Context, &Run) != 0) {
        if ((Run.Flags & KBTS_BREAK_FLAG_LINE_HARD) != 0) {

        }

        kbts_glyph *RunGlyph = nullptr;
        while (kbts_GlyphIteratorNext(&Run.Glyphs, &RunGlyph) != 0) {
            const int32_t CodepointIndex = RunGlyph->UserIdOrCodepointIndex;
            kbts_shape_codepoint ShapeCodepoint;
            if (kbts_ShapeGetShapeCodepoint(Context, CodepointIndex, &ShapeCodepoint) == 0) {
                std::println("aah it's wrong!! idk how to handle the error rn!!");
            }

            if (auto it = glyphs.find(RunGlyph->Id); it != glyphs.end()) {
                const float advance_x = FT_MulFix(RunGlyph->AdvanceX, face->size->metrics.x_scale) >> 6;

                const float dpi_advance_x = advance_x * DPI.x;

                size.y = std::max(size.y, it->second.dpi_height);
                size.x += dpi_advance_x;
                max_descent = std::max(max_descent, it->second.dpi_descent);
            }
        }
    }

    return { size, max_descent,  0};
}

int32_t my_kbts_ShapeRun(kbts_shape_context *Context, kbts_run *Run) {
    ZoneScoped

    return kbts_ShapeRun(Context, Run);
}
