#include "client/rendering/renderer/WorldRenderer.h"
#include "utils/Logger.h"

WorldRenderer::WorldRenderer(const World& world) : world(world) {
    Error error = texture.load("resources/textures.png", 4);
    if(error.has()) {
        LOG_WARNING(error.message);
    }
}

void WorldRenderer::render(float lag, ShaderMatrix& sm) {
    if(world.dirty) {
        TypedBuffer<Triangle> buffer(100);
        for(int x = 0; x < world.getSize(); x++) {
            for(int y = 0; y < world.getHeight(); y++) {
                for(int z = 0; z < world.getSize(); z++) {
                    if(world.getBlock(x, y, z).getId() != 0) {
                        addCube(buffer, x, y, z);
                    }
                }
            }
        }
        mesh.build(buffer);
        world.dirty = false;
        LOG_DEBUG("world render update");
    }
    (void)lag;
    texture.bindTo(0);
    for(int x = -1; x <= 1; x++) {
        for(int z = -1; z <= 1; z++) {
            sm.translateTo(world.getSize() * x, 0.0f, world.getSize() * z)
                .update();
            mesh.draw();
        }
    }
}

bool WorldRenderer::isAir(int x, int y, int z) const {
    return world.getBlock(x, y, z).getId() == 0;
}

void WorldRenderer::addCube(TypedBuffer<Triangle>& buffer, float x, float y,
                            float z) {
    Vector3 v000(x, y, z);
    Vector3 v001(x, y, z + 1);
    Vector3 v010(x, y + 1, z);
    Vector3 v011(x, y + 1, z + 1);
    Vector3 v100(x + 1, y, z);
    Vector3 v101(x + 1, y, z + 1);
    Vector3 v110(x + 1, y + 1, z);
    Vector3 v111(x + 1, y + 1, z + 1);

    const float ERROR = 0.0001f;
    Vector2 t1(0.1875f + ERROR, 0.0f + ERROR);
    Vector2 t2(0.25f - ERROR, 0.0f + ERROR);
    Vector2 t3(0.25f - ERROR, 0.0625f - ERROR);
    Vector2 t4(0.1875f + ERROR, 0.0625f - ERROR);

    if(isAir(x, y - 1, z)) {
        Vector2 tb(0.125f, 0.0625f);
        buffer.add(Triangle(Vertex(v000, Vector2(0.125f, 0.0f)),
                            Vertex(v100, t1), Vertex(v001, tb)));
        buffer.add(
            Triangle(Vertex(v100, t1), Vertex(v101, t4), Vertex(v001, tb)));
    }
    if(isAir(x, y + 1, z)) {
        Vector2 tt(0.3125f, 0.0f);
        buffer.add(
            Triangle(Vertex(v010, t2), Vertex(v011, t3), Vertex(v110, tt)));
        buffer.add(Triangle(Vertex(v110, tt), Vertex(v011, t3),
                            Vertex(v111, Vector2(0.3125f, 0.0625f))));
    }
    if(isAir(x - 1, y, z)) {
        buffer.add(
            Triangle(Vertex(v000, t4), Vertex(v001, t3), Vertex(v010, t1)));
        buffer.add(
            Triangle(Vertex(v001, t3), Vertex(v011, t2), Vertex(v010, t1)));
    }
    if(isAir(x + 1, y, z)) {
        buffer.add(
            Triangle(Vertex(v100, t3), Vertex(v110, t2), Vertex(v101, t4)));
        buffer.add(
            Triangle(Vertex(v101, t4), Vertex(v110, t2), Vertex(v111, t1)));
    }
    if(isAir(x, y, z + 1)) {
        buffer.add(
            Triangle(Vertex(v001, t4), Vertex(v101, t3), Vertex(v011, t1)));
        buffer.add(
            Triangle(Vertex(v111, t2), Vertex(v011, t1), Vertex(v101, t3)));
    }
    if(isAir(x, y, z - 1)) {
        buffer.add(
            Triangle(Vertex(v000, t3), Vertex(v010, t2), Vertex(v100, t4)));
        buffer.add(
            Triangle(Vertex(v110, t1), Vertex(v100, t4), Vertex(v010, t2)));
    }
}