
// Some util function to render text in the demo.  This is based on the
// code from Chipmunk (https://chipmunk-physics.net).

#include <GL/glew.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "VeraMoBd_ttf.h"

#define max(x, y) ((x) >= (y) ? (x) : (y))
#define SCALE 0.70f
#define LINE_HEIGHT (18.0f * SCALE)

typedef struct {
    GLuint prog;
    GLuint texture;

    GLuint u_proj_l;
    GLuint u_color_l;
    GLuint u_tex_l;
    GLuint a_pos_l;
    GLuint a_tex_pos_l;
} font_prog_t;

static font_prog_t font_prog;

// char -> glyph indexes generated by the lonesock tool.
static int glyph_indexes[256];

static const char *VSHADER =
    "uniform   mat4 u_proj;                                     \n"
    "attribute vec2 a_pos;                                      \n"
    "attribute vec2 a_tex_pos;                                  \n"
    "varying   vec2 v_tex_pos;                                  \n"
    "                                                           \n"
    "void main(void) {                                          \n"
    "    gl_Position = u_proj * vec4(a_pos, 0.0, 1.0);          \n"
    "    v_tex_pos = a_tex_pos;                                 \n"
    "}                                                          \n"
;

static const char *FSHADER =
    "uniform vec4      u_color;                                 \n"
    "uniform sampler2D u_tex;                                   \n"
    "varying vec2      v_tex_pos;                               \n"
    "                                                           \n"
    "float aa_step(float t1, float t2, float f)                 \n"
    "{                                                          \n"
    "    return smoothstep(t1, t2, f);                          \n"
    "}                                                          \n"
    "                                                           \n"
    "void main(void)                                            \n"
    "{                                                          \n"
    "    float sdf = texture2D(u_tex, v_tex_pos).a;             \n"
    "    float fw = length(vec2(dFdx(sdf), dFdy(sdf))) * 0.5;   \n"
    "    float alpha = aa_step(0.5 - fw, 0.5 + fw, sdf);        \n"
    "    gl_FragColor = u_color*(u_color.a * alpha);            \n"
    "}                                                          \n"
;

void font_init(const float proj_mat[16])
{
    GLuint vshader, fshader;
    int i, char_index;

    vshader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vshader, 1, &VSHADER, NULL);
    glCompileShader(vshader);

    fshader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fshader, 1, &FSHADER, NULL);
    glCompileShader(fshader);

    font_prog.prog = glCreateProgram();
    glAttachShader(font_prog.prog, vshader);
    glAttachShader(font_prog.prog, fshader);
    glLinkProgram(font_prog.prog);
    glUseProgram(font_prog.prog);

    font_prog.u_proj_l    = glGetUniformLocation(font_prog.prog, "u_proj");
    font_prog.u_color_l   = glGetUniformLocation(font_prog.prog, "u_color");
    font_prog.u_tex_l     = glGetUniformLocation(font_prog.prog, "u_tex");
    font_prog.a_pos_l     = glGetAttribLocation(font_prog.prog,  "a_pos");
    font_prog.a_tex_pos_l = glGetAttribLocation(font_prog.prog,  "a_tex_pos");

    glUniform1i(font_prog.u_tex_l, 0);
    glUniform4f(font_prog.u_color_l, 1, 1, 1, 1);
    glUniformMatrix4fv(font_prog.u_proj_l, 1, 0, proj_mat);

    // Load the SDF font texture.
    glGenTextures(1, &font_prog.texture);
    glBindTexture(GL_TEXTURE_2D, font_prog.texture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, sdf_tex_width, sdf_tex_height,
                 0, GL_ALPHA, GL_UNSIGNED_BYTE, sdf_data);
    glGenerateMipmap(GL_TEXTURE_2D);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
                                   GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

    // Fill in the glyph index array.
    for (i = 0; i < sdf_num_chars; i++) {
        char_index = sdf_spacing[i*8];
        glyph_indexes[char_index] = i;
    }
}

typedef struct {
    GLfloat pos[2];
    GLfloat tex_pos[2];
} font_vertex_t;

static GLsizei triangle_capacity = 0;
static GLsizei triangle_count = 0;
static font_vertex_t *triangle_buffer = NULL;

static font_vertex_t *font_push_triangles(GLsizei count)
{
    font_vertex_t *buffer;
    if (triangle_count + count > triangle_capacity) {
        triangle_capacity += max(triangle_capacity, count);
        triangle_buffer = (font_vertex_t *)
            realloc(triangle_buffer, triangle_capacity * 3 * sizeof(*buffer));
    }
    buffer = triangle_buffer + 3 * triangle_count;
    triangle_count += count;
    return buffer;
}

static GLfloat font_push_char(int character, GLfloat x, GLfloat y)
{
    int i = glyph_indexes[character];
    GLfloat w = (GLfloat)sdf_tex_width;
    GLfloat h = (GLfloat)sdf_tex_height;

    GLfloat gw = (GLfloat)sdf_spacing[i*8 + 3];
    GLfloat gh = (GLfloat)sdf_spacing[i*8 + 4];

    GLfloat txmin = sdf_spacing[i*8 + 1]/w;
    GLfloat tymin = sdf_spacing[i*8 + 2]/h;
    GLfloat txmax = txmin + gw/w;
    GLfloat tymax = tymin + gh/h;

    GLfloat s = SCALE / scale_factor;
    GLfloat xmin = x + sdf_spacing[i*8 + 5]/scale_factor * SCALE;
    GLfloat ymin = y + (sdf_spacing[i*8 + 6]/scale_factor - gh) * SCALE;
    GLfloat xmax = xmin + gw * SCALE;
    GLfloat ymax = ymin + gh * SCALE;

    font_vertex_t a = {{xmin, ymin}, {txmin, tymax}};
    font_vertex_t b = {{xmin, ymax}, {txmin, tymin}};
    font_vertex_t c = {{xmax, ymax}, {txmax, tymin}};
    font_vertex_t d = {{xmax, ymin}, {txmax, tymax}};

    font_vertex_t *triangles = font_push_triangles(2);
    triangles[0] = a;
    triangles[1] = b;
    triangles[2] = c;
    triangles[3] = a;
    triangles[4] = c;
    triangles[5] = d;

    return sdf_spacing[i * 8 + 7] * s;
}

void font_draw_text(float x, float y, const char *str)
{
    int i, len;
    GLfloat cx = (GLfloat)x;
    GLfloat cy = (GLfloat)y;
    for (i = 0, len = strlen(str); i < len; i++) {
        if (str[i] == '\n'){
            cy -= LINE_HEIGHT;
            cx = (GLfloat)x;
        } else {
            cx += (GLfloat)font_push_char(str[i], cx, cy);
        }
    }
}

void font_flush(void)
{
    glDisable(GL_STENCIL_TEST);
    glDisable(GL_DEPTH_TEST);
    glUseProgram(font_prog.prog);
    glEnableVertexAttribArray(font_prog.a_pos_l);
    glEnableVertexAttribArray(font_prog.a_tex_pos_l);
    glVertexAttribPointer(font_prog.a_pos_l, 2, GL_FLOAT, false,
                          sizeof(*triangle_buffer),
                          (void*)(&triangle_buffer->pos));
    glVertexAttribPointer(font_prog.a_tex_pos_l, 2, GL_FLOAT, false,
                          sizeof(*triangle_buffer),
                          (void*)(&triangle_buffer->tex_pos));
    glDrawArrays(GL_TRIANGLES, 0, triangle_count * 3);
    glDisableVertexAttribArray(font_prog.a_pos_l);
    glDisableVertexAttribArray(font_prog.a_tex_pos_l);
    glEnable(GL_DEPTH_TEST);
    triangle_count = 0;
}
