#include "textbuffer.hh"
#include "material.hh"

tms::gbuffer *textbuffer::verts;
tms::gbuffer *textbuffer::verts2;
tms::gbuffer *textbuffer::indices;
tms::varray *textbuffer::va;
tms::varray *textbuffer::va2;
tms::mesh *textbuffer::mesh;
tms::mesh *textbuffer::mesh2;
tms::entity *textbuffer::e;
tms::entity *textbuffer::e2;

int textbuffer::n = 0;
int textbuffer::n2 = 0;

struct textbuf_vert {
    tvec3 pos;
    tvec2 uv;
    tvec4 color;
};

textbuf_vert textbuffer::base[4];

#define TEX_WIDTH  1024.f
#define TEX_HEIGHT 1024.f

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

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

    verts2 = new tms::gbuffer(4*TEXTBUFFER_MAX*sizeof(struct textbuf_vert));
    verts2->usage = TMS_GBUFFER_STREAM_DRAW;

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

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

    va2 = new tms::varray(3);
    va2->map_attribute("position", 3, GL_FLOAT, verts2);
    va2->map_attribute("uv", 2, GL_FLOAT, verts2);
    va2->map_attribute("vcolor", 4, GL_FLOAT, verts2);

    uint16_t *i = (uint16_t*)indices->get_buffer();
    for (int x=0; x<TEXTBUFFER_MAX; x++) {
        int o = x*6;
        int 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] = (textbuf_vert){
        (tvec3){.5f,.5f,0.f},
        (tvec2){.5f, 1.f},
        (tvec4){0.f, 0.f, 0.f, 0.f},
    };
    base[1] = (textbuf_vert){
        (tvec3){-.5f,.5f,0.f},
        (tvec2){0.f, 1.f},
        (tvec4){0.f, 0.f, 0.f, 0.f},
    };
    base[2] = (textbuf_vert){
        (tvec3){-.5f,-.5f,0.f},
        (tvec2){0.f, .5f},
        (tvec4){0.f, 0.f, 0.f, 0.f},
    };
    base[3] = (textbuf_vert){
        (tvec3){.5f,-.5f,0.f},
        (tvec2){.5f, .5f},
        (tvec4){0.f, 0.f, 0.f, 0.f},
    };

    reset();
}

void
textbuffer::reset()
{
    n = 0;
    n2 = 0;
}

void
textbuffer::upload()
{
    if (mesh) {
        mesh->i_start = 0;
        mesh->i_count = n*6;
    }
    if (mesh2) {
        mesh2->i_start = 0;
        mesh2->i_count = n2*6;
    }

    if (n) verts->upload_partial(n*4*sizeof(struct textbuf_vert));
    if (n2) verts2->upload_partial(n2*4*sizeof(struct textbuf_vert));
}

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

    e = new tms::entity();
    mesh = new tms::mesh(va, indices);

    e->prio = 3;
    e->set_mesh(mesh);
    e->set_material(&m_charbuf);

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

    return e;
}

void
custom_update(struct tms_entity *e)
{
    tms_debugf("hello");
}

tms::entity *
textbuffer::get_entity2()
{
    if (e2) return e2;

    mesh2 = new tms::mesh(va2, indices);
    mesh2->i_start = 0;
    mesh2->i_count = n2*9*3;

    e2 = new tms::entity();
    e2->prio = 0;
    e2->set_mesh(mesh2);
    e2->set_material(&m_charbuf2);

    return e2;
}

void
textbuffer::add_char(glyph *gl,
        float x, float y, float z,
        float r, float g, float b, float a,
        float w, float h)
{
    tms_sprite *s = gl->sprite;
    int bx = 505;
    int by = 950;
    int tx = 530;
    int ty = 1024;

    tvec2 uvb = {(float)bx / TEX_WIDTH, (float)by / TEX_HEIGHT};
    tvec2 uvt = {(float)tx / TEX_WIDTH, (float)ty / TEX_HEIGHT};

    if (n < TEXTBUFFER_MAX) {
        textbuf_vert *_b = (textbuf_vert *)verts->get_buffer();
        for (int ix=0; ix<4; ix++) {
            _b[n*4+ix] = base[ix];

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

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

            _b[n*4+ix].color.r = r;
            _b[n*4+ix].color.g = g;
            _b[n*4+ix].color.b = b;
            _b[n*4+ix].color.a = a;

            float x = ((ix == 0 || ix == 3) ? s->tr.x : s->bl.x);
            float y = (ix < 2 ? s->tr.y : s->bl.y);

            _b[n*4+ix].uv.x = x;
            _b[n*4+ix].uv.y = y;
        }

        n++;
    }
}

void
textbuffer::add_bg(
        float x, float y, float z,
        float r, float g, float b, float a,
        float w, float h)
{
    if (n < TEXTBUFFER_MAX) {
        textbuf_vert *_b = (textbuf_vert *)verts->get_buffer();
        for (int ix=0; ix<4; ix++) {
            _b[n*4+ix] = base[ix];

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

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

            _b[n*4+ix].color.r = r;
            _b[n*4+ix].color.g = g;
            _b[n*4+ix].color.b = b;
            _b[n*4+ix].color.a = a;

            _b[n*4+ix].uv.x = .001;
            _b[n*4+ix].uv.y = .999;
        }

        n++;
    }
}

void
textbuffer::add_char2(glyph *gl,
        float x, float y, float z,
        float r, float g, float b, float a,
        float w, float h)
{
    tms_sprite *s = gl->sprite;
    int bx = 505;
    int by = 950;
    int tx = 530;
    int ty = 1024;

    tvec2 uvb = {(float)bx / TEX_WIDTH, (float)by / TEX_HEIGHT};
    tvec2 uvt = {(float)tx / TEX_WIDTH, (float)ty / TEX_HEIGHT};

    if (n2 < TEXTBUFFER_MAX) {
        textbuf_vert *_b = (textbuf_vert *)verts2->get_buffer();
        for (int ix=0; ix<4; ix++) {
            _b[n2*4+ix] = base[ix];

            _b[n2*4+ix].pos.x *= w;
            _b[n2*4+ix].pos.y *= h;

            _b[n2*4+ix].pos.x += x;
            _b[n2*4+ix].pos.y += y;
            _b[n2*4+ix].pos.z += z;

            _b[n2*4+ix].color.r = r;
            _b[n2*4+ix].color.g = g;
            _b[n2*4+ix].color.b = b;
            _b[n2*4+ix].color.a = a;

            float x = ((ix == 0 || ix == 3) ? s->tr.x : s->bl.x);
            float y = (ix < 2 ? s->tr.y : s->bl.y);

            _b[n2*4+ix].uv.x = x;
            _b[n2*4+ix].uv.y = y;
        }

        n2++;
    }
}

void
textbuffer::add_text(p_text *text, float scale)
{
    p_text::text_glyph *g = 0;
    for (int i=0; i<text->num_glyphs; ++i) {
        g = &text->glyphs[i];

        textbuffer::add_char(g->parent,
                g->x, g->y,
                2.0,
                1.0, 1.0, 1.0, 1.0,
                scale * g->get_bw(),
                scale * g->get_bh()
                );
    }
}

void
textbuffer::add_text(const char *text, p_font *font,
        float x, float y, float z,
        float r, float g, float b, float a,
        float scale,
        uint8_t horizontal_align/*=ALIGN_CENTER*/,
        uint8_t vertical_align/*=ALIGN_CENTER*/,
        bool bg/*=false*/
        )
{
    float width = 0.f;
    float max_width = 0.f;
    float line_height = font->get_height() * scale;
    float height = line_height;
    int slen = strlen(text);
    struct glyph *gl;

    for (int i=0; i<slen; ++i) {
        if ((gl = font->get_glyph(text[i]))) {
            if (gl->newline) {
                height += line_height;
                width = 0.f;
                continue;
            }

            width += gl->ax * scale;

            if (width > max_width) {
                max_width = width;
            }
        }
    }

    if (width == 0.f) {
        height -= line_height;
    }

    width = max_width;

    float mid_x = x;
    float mid_y = y;

    if (horizontal_align == ALIGN_CENTER) {
        x -= max_width/2.f;
    } else if (horizontal_align == ALIGN_RIGHT) {
        x -= max_width;
    }

    float base_x = x;

    if (vertical_align == ALIGN_CENTER) {
        y += height/2.f;
    } else if (vertical_align == ALIGN_TOP) {
        y -= height;
    }

    if (bg) {
        textbuffer::add_bg(mid_x,mid_y+line_height*.75f,z,0,0,0,.5*a, max_width+.1, height+.1);
    }

    for (int i=0; i<slen; ++i) {
        if ((gl = font->get_glyph(text[i]))) {
            float x2 = x + (gl->bl + gl->bw/2.f) * scale;
            float y2 = y + (gl->bt - gl->bh/2.f) * scale;

            if (gl->newline) {
                x = base_x;
                y -= line_height;
                continue;
            }

            x += gl->ax * scale;
            textbuffer::add_char(gl,
                    x2,
                    y2,
                    z + 0.01f,
                    r, g, b, a,
                    gl->bw*scale,
                    gl->bh*scale
                    );
        }
    }
}

void
textbuffer::add_text2(const char *text, p_font *font,
        float x, float y, float z,
        float r, float g, float b, float a,
        float scale,
        uint8_t horizontal_align/*=ALIGN_CENTER*/,
        uint8_t vertical_align/*=ALIGN_CENTER*/
        )
{
    float width = 0.f;
    float height = font->get_height() * scale;
    int slen = strlen(text);
    struct glyph *gl;

    for (int i=0; i<slen; ++i) {
        if ((gl = font->get_glyph(text[i]))) {
            width += gl->ax * scale;
        }
    }

    if (horizontal_align == ALIGN_CENTER) {
        x -= width/2.f;
    } else if (horizontal_align == ALIGN_RIGHT) {
        x -= width;
    }

    if (vertical_align == ALIGN_CENTER) {
        y -= height/2.f;
    } else if (vertical_align == ALIGN_TOP) {
        y -= height;
    }

    for (int i=0; i<slen; ++i) {
        if ((gl = font->get_glyph(text[i]))) {
            float x2 = x + (gl->bl + gl->bw/2.f) * scale;
            float y2 = y + (gl->bt - gl->bh/2.f) * scale;

            x += gl->ax * scale;
            textbuffer::add_char2(gl,
                    x2,
                    y2,
                    z + 0.01f,
                    .0f, .0f, .0f, 1.f,
                    gl->bw*scale,
                    gl->bh*scale
                    );
        }
    }
}

#undef TEX_WIDTH
#undef TEX_HEIGHT
