#include "render/TextRenderer.h"
#include <ft2build.h>
#include FT_FREETYPE_H

#include "Environment.h"
#include "render/OpenGlExtensions.h"
#include <iostream>
#include <array>

namespace ZL {

TextRenderer::~TextRenderer()
{
    for (auto& kv : glyphs) {
        if (kv.second.texID) glDeleteTextures(1, &kv.second.texID);
    }
    glyphs.clear();

    if (vbo) glDeleteBuffers(1, &vbo);
    // if (vao) glDeleteVertexArrays(1, &vao);
    vao = 0;
    vbo = 0;
}

bool TextRenderer::init(Renderer& renderer, const std::string& ttfPath, int pixelSize)
{
    r = &renderer;

    r->shaderManager.AddShaderFromFiles(shaderName,
        "resources/shaders/text2d.vertex",
        "resources/shaders/text2d.fragment",
        "" // ZIP пустой
    );

    if (!loadGlyphs(ttfPath, pixelSize)) return false;

    // VAO/VBO для 6 вершин (2 треугольника) * (pos.xy + uv.xy) = 4 float
    // glGenVertexArrays(1, &vao);
    glGenBuffers(1, &vbo);

    //glBindVertexArray(vao);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, nullptr, GL_DYNAMIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);


    return true;
}

bool TextRenderer::loadGlyphs(const std::string& ttfPath, int pixelSize)
{
    FT_Library ft;
    if (FT_Init_FreeType(&ft)) {
        std::cerr << "FreeType: FT_Init_FreeType failed\n";
        return false;
    }

    FT_Face face;
    if (FT_New_Face(ft, ttfPath.c_str(), 0, &face)) {
        std::cerr << "FreeType: failed to load font: " << ttfPath << "\n";
        FT_Done_FreeType(ft);
        return false;
    }

    FT_Set_Pixel_Sizes(face, 0, pixelSize);

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    glyphs.clear();
    for (unsigned char c = 32; c < 128; ++c) {
        if (FT_Load_Char(face, c, FT_LOAD_RENDER)) {
            continue;
        }

        unsigned int tex;
        glGenTextures(1, &tex);
        glBindTexture(GL_TEXTURE_2D, tex);
        glTexImage2D(
            GL_TEXTURE_2D,
            0,
            GL_RED,
            face->glyph->bitmap.width,
            face->glyph->bitmap.rows,
            0,
            GL_RED,
            GL_UNSIGNED_BYTE,
            face->glyph->bitmap.buffer
        );

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        GlyphInfo g;
        g.texID = tex;
        g.size = Eigen::Vector2f((float)face->glyph->bitmap.width, (float)face->glyph->bitmap.rows);
        g.bearing = Eigen::Vector2f((float)face->glyph->bitmap_left, (float)face->glyph->bitmap_top);
        g.advance = (unsigned int)face->glyph->advance.x;

        glyphs.emplace((char)c, g);
    }

    FT_Done_Face(face);
    FT_Done_FreeType(ft);

    glBindTexture(GL_TEXTURE_2D, 0);
    return true;
}

void TextRenderer::drawText(const std::string& text, float x, float y, float scale, bool centered, std::array<float, 4> color)
{
    if (!r) return;

    // Считаем ширину строки для центрирования
    float totalW = 0.0f;
    if (centered) {
        for (char ch : text) {
            auto it = glyphs.find(ch);
            if (it == glyphs.end()) continue;
            totalW += (it->second.advance >> 6) * scale;
        }
        x -= totalW * 0.5f;
    }

    r->shaderManager.PushShader(shaderName);

    // Орто-проекция в пикселях: (0..W, 0..H)
    float W = (float)Environment::width;
    float H = (float)Environment::height;

    Eigen::Matrix4f proj = Eigen::Matrix4f::Identity();
    proj(0,0) = 2.0f / W;
    proj(1,1) = 2.0f / H;
    proj(0,3) = -1.0f;
    proj(1,3) = -1.0f;

    // uProjection
    r->RenderUniformMatrix4fv("uProjection", false, proj.data());
    r->RenderUniform1i("uText", 0);

    r->RenderUniform4fv("uColor", color.data());

    glActiveTexture(GL_TEXTURE0);
    // glBindVertexArray(vao);

    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    r->EnableVertexAttribArray("vPosition");
    r->EnableVertexAttribArray("vTexCoord");

    r->VertexAttribPointer2fv("vPosition", 4 * sizeof(float), (const char*)0);
    r->VertexAttribPointer2fv("vTexCoord", 4 * sizeof(float), (const char*)(2 * sizeof(float)));

    float penX = x;
    float penY = y;

    for (char ch : text) {
        auto it = glyphs.find(ch);
        if (it == glyphs.end()) continue;

        const GlyphInfo& g = it->second;

        float xpos = penX + g.bearing.x() * scale;
        float ypos = penY - (g.size.y() - g.bearing.y()) * scale;

        float w = g.size.x() * scale;
        float h = g.size.y() * scale;

        // 2 треугольника
        float verts[6][4] = {
            { xpos,     ypos + h, 0.0f, 0.0f },
            { xpos,     ypos,     0.0f, 1.0f },
            { xpos + w, ypos,     1.0f, 1.0f },

            { xpos,     ypos + h, 0.0f, 0.0f },
            { xpos + w, ypos,     1.0f, 1.0f },
            { xpos + w, ypos + h, 1.0f, 0.0f }
        };

        glBindTexture(GL_TEXTURE_2D, g.texID);
        glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(verts), verts);
        glDrawArrays(GL_TRIANGLES, 0, 6);

        penX += (g.advance >> 6) * scale;
    }

    // cleanup
    glBindTexture(GL_TEXTURE_2D, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    r->DisableVertexAttribArray("vPosition");
    r->DisableVertexAttribArray("vTexCoord");

    r->shaderManager.PopShader();
}

} // namespace ZL