#include "pch.h"
#include "log.h"
#include "font.h"
#include "shader.h"
#include "asset.h"

std::map<kFont, Font> FontManager::m_fonts;
Sampler FontManager::m_sampler;

bool Font::load(const char* ttf, int font_size)
{
    Asset file;
    LOG("Font::load %s", ttf);
    if (file.open(ttf) && file.read_all())
    {
        LOG("Font::load loaded");
        auto bitmap = std::make_unique<uint8_t[]>(w*h);
        chars.resize(num_chars);
        stbtt_BakeFontBitmap(file.m_data, 0, (float)font_size*2, bitmap.get(), w, h, start_char, num_chars, chars.data());
        calc_bounds();
        font_tex.create(w, h, GL_R8, GL_RED, bitmap.get());
        file.close();
        size = font_size;
        return true;
    }
    return false;
}

void Font::calc_bounds()
{
    glm::vec2 bbmin(FLT_MAX);
    glm::vec2 bbmax(-FLT_MAX);
    for (int i = 0; i < num_chars; i++)
    {
        stbtt_aligned_quad q;
        float x = 0, y = 0;
        stbtt_GetBakedQuad(chars.data(), w, h, i, &x, &y, &q, true);
        bbmin = glm::min(bbmin, { q.x0 / 2.f, q.y0 / 2.f });
        bbmax = glm::max(bbmax, { q.x1 / 2.f, q.y1 / 2.f });
    }
    bounds = { glm::vec4(bbmin, bbmax) };
}

void FontManager::init()
{
    m_sampler.create();
}

bool FontManager::load(kFont id, const char* ttf, int sz)
{
    return m_fonts[id].load(ttf, sz);
}

const Font& FontManager::get(kFont id)
{
    return m_fonts[id];
}

bool TextMesh::create()
{
    glGenBuffers(2, font_buffers);
#if USE_VBO
    glGenVertexArrays(1, &font_array);
    glBindVertexArray(font_array);
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, font_buffers[1]);
    glBindBuffer(GL_ARRAY_BUFFER, font_buffers[0]);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (GLvoid*)0);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (GLvoid*)(sizeof(float)*2));
    glBindVertexArray(0);
#endif // USE_VBO
    return true;
}

void TextMesh::update(kFont id, const char* text)
{
    font_id = id;
    auto& f = FontManager::get(id);
    if (f.chars.size())
    {
        const auto len = strlen(text);
        float x = 0;
        float y = 0;
        std::vector<glm::vec4> v;
        std::vector<GLushort> idx;
        glm::vec2 bbmin(FLT_MAX);
        glm::vec2 bbmax(-FLT_MAX);
        for (int i = 0; i < len; i++)
        {
            if (text[i] == '\n')
            {
                x = 0;
                y += f.size * 2;
                continue;
            }
            if (max_width > 0 && x > max_width * 2 /*font scale factor*/)
            {
                x = 0;
                y += f.size * 2;
            }
            int c = text[i] - f.start_char;
            stbtt_aligned_quad q;
            stbtt_GetBakedQuad((stbtt_bakedchar*)f.chars.data(), f.w, f.h, c, &x, &y, &q, true);
            auto n = (int)v.size();
            v.emplace_back(q.x0/2.f, q.y1/2.f, q.s0, q.t1);
            v.emplace_back(q.x0/2.f, q.y0/2.f, q.s0, q.t0);
            v.emplace_back(q.x1/2.f, q.y0/2.f, q.s1, q.t0);
            v.emplace_back(q.x1/2.f, q.y1/2.f, q.s1, q.t1);
            idx.push_back(n+0);
            idx.push_back(n+1);
            idx.push_back(n+2);
            idx.push_back(n+0);
            idx.push_back(n+2);
            idx.push_back(n+3);
            bbmin = glm::min(bbmin, { q.x0/2.f, q.y0/2.f });
            bbmax = glm::max(bbmax, { q.x1/2.f, q.y1/2.f });
        }
        for (auto& vi : v)
            vi -= glm::vec4(bbmin, 0, 0);
        bb = bbmax - bbmin;
        font_array_count = (int)idx.size();
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, font_buffers[1]);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx.size() * sizeof(GLushort), idx.data(), GL_STATIC_DRAW);
        glBindBuffer(GL_ARRAY_BUFFER, font_buffers[0]);
        glBufferData(GL_ARRAY_BUFFER, v.size() * sizeof(glm::vec4), v.data(), GL_STATIC_DRAW);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    }
}

void TextMesh::draw()
{
    auto& f = FontManager::get(font_id);
    if (f.font_tex.ready())
    {
        glActiveTexture(GL_TEXTURE0);
        f.font_tex.bind();
        FontManager::m_sampler.bind(0);

#if USE_VBO
        glBindVertexArray(font_array);
        glDrawElements(GL_TRIANGLES, font_array_count, GL_UNSIGNED_SHORT, 0);
        glBindVertexArray(0);
#else
        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, font_buffers[1]);
        glBindBuffer(GL_ARRAY_BUFFER, font_buffers[0]);
        glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (GLvoid*)0);
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (GLvoid*)(sizeof(float) * 2));
        glDrawElements(GL_TRIANGLES, font_array_count, GL_UNSIGNED_SHORT, 0);
        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
#endif // USE_VBO

        f.font_tex.unbind();
        FontManager::m_sampler.unbind();
    }
}
