#include "client/rendering/Renderer.h"
#include "rendering/Attributes.h"
#include "utils/Buffer.h"

Renderer::Renderer()
    : buffer(16), font("resources/font8x8.png"), text(nullptr), index(0),
      maxIndex(0), x(0.0f), y(0.0f), vertices(0) {
    vertexBuffer.setAttributes(
        Attributes().addFloat(2).addFloat(2).addColor4());
    vertexBuffer.setStreamData(1024 * 1024);
}

void Renderer::setupStringRead(const char* s, int limit) {
    text = s;
    index = 0;
    maxIndex = limit;
}

void Renderer::setupString(const Vector2& pos, const char* s, int limit) {
    buffer.clear();
    setupStringRead(s, limit);
    x = pos[0];
    y = pos[1];
    vertices = 0;
    color = {0xFF, 0xFF, 0xFF, 0x00};
}

bool Renderer::hasStringData() const {
    return (index < maxIndex || maxIndex < 0) && text[index] != '\0';
}

char32_t Renderer::readUnicode() {
    char32_t c = text[index];
    index++;
    if(c > 128 && hasStringData()) {
        c = (text[index] & 0x3F) | ((c & 0x1F) << 6);
        index++;
    }
    return c;
}

bool Renderer::readColor(char32_t c) {
    if(c != '&') {
        return false;
    }
    for(int i = 0; i < 3; i++) {
        if(!hasStringData()) {
            return true;
        }
        color[i] = ((text[index] - '0') * 255) / 9;
        index++;
    }
    return true;
}

void Renderer::addChar(char32_t c) {
    float minX = (c & 0xF) * (1.0f / 16.0f) + (1.0f / 128.0f);
    float minY = (c >> 4) * (1.0f / 16.0f);
    float maxX = minX + (1.0f / 16.0f) - (2.0f / 128.0f);
    float maxY = minY + (1.0f / 16.0f);
    buffer.add(x).add(y).add(minX).add(minY).add(color);
    buffer.add(x).add(y + 8.0f).add(minX).add(maxY).add(color);
    buffer.add(x + 6.0f).add(y).add(maxX).add(minY).add(color);
    buffer.add(x + 6.0f).add(y + 8.0f).add(maxX).add(maxY).add(color);
    x += 6;
    vertices += 4;
}

void Renderer::renderString(const Vector2& pos, const char* text, int limit) {
    setupString(pos, text, limit);
    while(hasStringData()) {
        char32_t c = readUnicode();
        if(readColor(c)) {
            continue;
        }
        addChar(c);
    }
    font.bindTo(0);
    update();
    vertexBuffer.drawStrip(vertices);
}

Vector2 Renderer::getStringSize(const char* text, int limit) {
    setupStringRead(text, limit);
    Vector2 size(0.0f, 8.0f);
    while(hasStringData()) {
        char32_t c = readUnicode();
        if(readColor(c)) {
            continue;
        }
        size[0] += 6.0f;
    }
    return size;
}

void Renderer::renderRectangle(const Vector2& pos, const Vector2& size,
                               Color4 c) {
    buffer.clear();
    buffer.add(pos[0]).add(pos[1]).add(0.0f).add(0.0f).add(c);
    buffer.add(pos[0]).add(pos[1] + size[1]).add(0.0f).add(0.0f).add(c);
    buffer.add(pos[0] + size[0]).add(pos[1]).add(0.0f).add(0.0f).add(c);
    buffer.add(pos[0] + size[0])
        .add(pos[1] + size[1])
        .add(0.0f)
        .add(0.0f)
        .add(c);
    update();
    vertexBuffer.drawStrip(4);
}

void Renderer::update() {
    vertexBuffer.updateData(
        0, std::min(buffer.getLength(), vertexBuffer.getSize()), buffer);
}

int Renderer::charsInSpace(float width) {
    return std::abs(width / 6.0f);
}