#include "client/rendering/FontRenderer.h"

const size_t FontRenderer::BUFFER_LENGTH = 8 * 1024 * 1024;

FontRenderer::FontRenderer() : tex("resources/font8x8.png"), offset(BUFFER_LENGTH), vba(0), vbo(0) {
    glGenVertexArrays(1, &vba);
    glBindVertexArray(vba);

    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof (float) * 7, static_cast<float*> (0));
    glEnableVertexAttribArray(0);

    glVertexAttribPointer(1, 2, GL_FLOAT, false, sizeof (float) * 7, static_cast<float*> (0) + 2);
    glEnableVertexAttribArray(1);

    glVertexAttribPointer(2, 3, GL_FLOAT, false, sizeof (float) * 7, static_cast<float*> (0) + 4);
    glEnableVertexAttribArray(2);
}

FontRenderer::~FontRenderer() {
    glDeleteVertexArrays(1, &vba);
    glDeleteBuffers(1, &vbo);
}

void FontRenderer::drawString(float x, float y, const char* text) {
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    const size_t maxIndex = 256;
    const size_t maxLength = maxIndex * 4 * sizeof (float) * 7;

    if(offset + maxLength >= BUFFER_LENGTH) {
        offset = 0;
        glBufferData(GL_ARRAY_BUFFER, BUFFER_LENGTH, nullptr, GL_STREAM_DRAW);
    }

    float* buffer = static_cast<float*> (glMapBufferRange(GL_ARRAY_BUFFER, offset, maxLength, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT));
    if(buffer == nullptr) {
        return;
    }

    size_t index = 0;
    size_t i = 0;
    float r = 1.0f;
    float g = 1.0f;
    float b = 1.0f;

    while(text[index] != '\0' && index < maxIndex) {
        char c = text[index];
        if(c == '&') {
            if(text[index + 1] == '\0' || text[index + 2] == '\0' || text[index + 3] == '\0') {
                break;
            }
            r = (text[index + 1] - '0') * (1.0f / 9.0f);
            g = (text[index + 2] - '0') * (1.0f / 9.0f);
            b = (text[index + 3] - '0') * (1.0f / 9.0f);
            index += 4;
            continue;
        }

        float minX = (c & 0xF) * (1.0f / 16.0f);
        float minY = (c >> 4) * (1.0f / 16.0f);
        float maxX = minX + (1.0f / 16.0f);
        float maxY = minY + (1.0f / 16.0f);

        buffer[i++] = x;
        buffer[i++] = y;
        buffer[i++] = minX;
        buffer[i++] = minY;
        buffer[i++] = r;
        buffer[i++] = g;
        buffer[i++] = b;

        buffer[i++] = x;
        buffer[i++] = y + 8;
        buffer[i++] = minX;
        buffer[i++] = maxY;
        buffer[i++] = r;
        buffer[i++] = g;
        buffer[i++] = b;

        buffer[i++] = x + 8;
        buffer[i++] = y;
        buffer[i++] = maxX;
        buffer[i++] = minY;
        buffer[i++] = r;
        buffer[i++] = g;
        buffer[i++] = b;

        buffer[i++] = x + 8;
        buffer[i++] = y + 8;
        buffer[i++] = maxX;
        buffer[i++] = maxY;
        buffer[i++] = r;
        buffer[i++] = g;
        buffer[i++] = b;

        x += 8;
        index++;
    }

    glUnmapBuffer(GL_ARRAY_BUFFER);
    glBindVertexArray(vba);
    tex.bind(0);
    glDrawArrays(GL_TRIANGLE_STRIP, offset / (sizeof (float) * 7), i / 7);
    offset += maxLength;
}