#include "fluidbuffer.hh"
#include "world.hh"

tms::gbuffer *fluidbuffer::verts;
tms::gbuffer *fluidbuffer::indices;
tms::varray *fluidbuffer::va;
tms::mesh *fluidbuffer::mesh;
tms::entity *fluidbuffer::e;
tms::entity *fluidbuffer::e2;

uint32_t fluidbuffer::n = 0;

struct fluidbuf_vert {
    tvec3 pos;
    tvec3 uv;
};

fluidbuf_vert fluidbuffer::base[4];

void fluidbuffer::reset()
{
    n = 0;
}

tms::entity *
fluidbuffer::get_entity()
{
    if (e) return e;

    mesh = new tms::mesh(va, indices);
    mesh->i32 = 1;

    e = new tms::entity();
    e->prio = 0;
    e->set_mesh(mesh);
    e->set_material(&m_fluidbuf);

    mesh->i_start = 0;
    mesh->i_count = n*9*3;

    return e;
}

void fluidbuffer::_init()
{
    tms_infof("Initializing fluidbuffer...");

    verts = new tms::gbuffer(4*(FLUIDBUFFER_MAX)*sizeof(struct fluidbuf_vert));
    verts->usage = TMS_GBUFFER_STREAM_DRAW;

    indices = new tms::gbuffer(6*FLUIDBUFFER_MAX*sizeof(uint32_t));
    indices->usage = TMS_GBUFFER_STATIC_DRAW;
    indices->target = GL_ELEMENT_ARRAY_BUFFER;

    va = new tms::varray(2);
    va->map_attribute("position", 3, GL_FLOAT, verts);
    va->map_attribute("uv", 3, GL_FLOAT, verts);

    uint32_t *i = (uint32_t*)indices->get_buffer();
    for (uint32_t x=0; x<FLUIDBUFFER_MAX; x++) {
        uint32_t o = x*6;
        uint32_t vo = x*4;

        i[o+0] = vo;
        i[o+1] = vo+1;
        i[o+2] = vo+2;
        i[o+3] = vo;
        i[o+4] = vo+2;
        i[o+5] = vo+3;
    }

    indices->upload();

    base[0] = (fluidbuf_vert){
        (tvec3){.5f,.5f,0.f},
        (tvec3){.5f, 1.f, 0.f}
    };
    base[1] = (fluidbuf_vert){
        (tvec3){-.5f,.5f,0.f},
        (tvec3){0.f, 1.f, 0.f},
    };
    base[2] = (fluidbuf_vert){
        (tvec3){-.5f,-.5f,0.f},
        (tvec3){0.f, .75f, 0.f},
    };
    base[3] = (fluidbuf_vert){
        (tvec3){.5f,-.5f,0.f},
        (tvec3){.5f, .75f, 0.f},
    };

    reset();
}

void fluidbuffer::add(
    float x, float y, float z,
    //float r, float g, float b, float a,
    float pressure,
    float w, float h
) {
    uint32_t particle_limit = ((W->level.version <= LEVEL_VERSION_1_5_1) ? FLUIDBUFFER_MAX_1_5_1 : FLUIDBUFFER_MAX);
    if (n >= particle_limit) return;

    fluidbuf_vert *vert_buf = (fluidbuf_vert *) verts->get_buffer();
    for (int ix=0; ix<4; ix++) {
        vert_buf[n*4+ix] = base[ix];

        vert_buf[n*4+ix].pos.x *= w;
        vert_buf[n*4+ix].pos.y *= h;

        vert_buf[n*4+ix].pos.x += x;
        vert_buf[n*4+ix].pos.y += y;
        vert_buf[n*4+ix].pos.z += z;

        vert_buf[n*4+ix].uv.x += .5f*(3%2);
        vert_buf[n*4+ix].uv.y -= .25f*(3/2);
        vert_buf[n*4+ix].uv.z = pressure;
    }
    n++;
}

void fluidbuffer::upload()
{
    if (mesh) {
        mesh->i_start = 0;
        mesh->i_count = n*6;
    }
    if (n) verts->upload_partial(n*4*sizeof(struct fluidbuf_vert));
}
