#define GLAD_GL_IMPLEMENTATION

#include "local.h"

static int run_render_width = 0;
static int run_render_height = 0;

ENGINE_ENUM(r_window_mode, 0, "",
  (windowed, 0, ""),
  (fullscreen, 1, ""),
  (borderless, 2, "")
)
ENGINE_INTEGER(r_monitor, -1, "Chosen monitor index for fullscreen, -1 gets the primary monitor")
ENGINE_INTEGER(r_window_width, 0, "desired width of window")
ENGINE_INTEGER(r_window_height, 0, "desired height of window")

GLFWwindow * run_glfw_window;

static uv_timer_t run_render_timer;

static void render_cb(uv_timer_t *handle);

void Engine__init_render(void) {
  if(!glfwInit()) {
    ALIAS_ERROR("glfw: failed to initialize");
    return;
  }

  glfwDefaultWindowHints();
  glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

  int width = r_window_width;
  int height = r_window_height;

  GLFWmonitor *primary = glfwGetPrimaryMonitor();
  
  int count;
  GLFWmonitor **monitors = glfwGetMonitors(&count);

  GLFWmonitor *monitor = r_monitor == -1 ? primary : monitors[alias_min(alias_max(r_monitor, 0), count)];

  const GLFWvidmode *mode = glfwGetVideoMode(monitor);

  if(r_window_mode == 0) {
    monitor = NULL;

    if(width == 0) {
      width = mode->width / 2;
    }

    if(height == 0) {
      height = mode->height / 2;
    }
  } else if(r_window_mode == 1) {
  } else if(r_window_mode == 2) {
    glfwWindowHint(GLFW_RED_BITS, mode->redBits);
    glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits);
    glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits);
    glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate);
    width = mode->width;
    height = mode->height;
  }

  run_glfw_window = glfwCreateWindow(width, height, "alias engine", monitor, NULL);
  if(run_glfw_window == NULL) {
    ALIAS_ERROR("glfw: could not create window");
    return;
  }

  glfwMakeContextCurrent(run_glfw_window);

  gladLoadGL(glfwGetProcAddress);

  uv_timer_init(&run_libuv_loop, &run_render_timer);
  uv_timer_start(&run_render_timer, render_cb, 1000 / 60, 1000 / 60);
}

static void _update_display(void);

static void render_cb(uv_timer_t *handle) {
  if(glfwWindowShouldClose(run_glfw_window) || Engine__should_exit) {
    uv_timer_stop(handle);
    return;
  }

  glfwPollEvents();

  Engine__update_input();

  if(!Engine__update_state()) {
    glfwSetWindowShouldClose(run_glfw_window, GLFW_TRUE);
    return;
  }

  _update_display();
}

uint32_t Engine_render_get_width(void) {
  return run_render_width;
}

uint32_t Engine_render_get_height(void) {
  return run_render_height;
}

DEFINE_COMPONENT(Camera)

DEFINE_COMPONENT(DrawRectangle)

DEFINE_COMPONENT(DrawCircle)

DEFINE_COMPONENT(DrawText)

DEFINE_COMPONENT(Sprite)

extern struct GL_ShaderResource u_time;
extern struct GL_ShaderResource u_model_matrix;
extern struct GL_ShaderResource u_view_matrix;
extern struct GL_ShaderResource u_model_view_matrix;
extern struct GL_ShaderResource u_projection_matrix;
extern struct GL_ShaderResource u_view_projection_matrix;
extern struct GL_ShaderResource u_model_view_projection_matrix;

static inline void prepare_model_view(void) {
  GL_matrix_multiply(u_view_matrix.uniform.data.mat, u_model_matrix.uniform.data.mat,
                     u_model_view_matrix.uniform.data.mat);
}

static inline void prepare_view_projection(void) {
  GL_matrix_multiply(u_projection_matrix.uniform.data.mat, u_view_matrix.uniform.data.mat,
                     u_view_projection_matrix.uniform.data.mat);
}

static inline void prepare_model_view_projection(void) {
  GL_ShaderResource_prepare(&u_view_projection_matrix);
  GL_matrix_multiply(u_view_projection_matrix.uniform.data.mat, u_model_matrix.uniform.data.mat,
                     u_model_view_projection_matrix.uniform.data.mat);
}

struct GL_ShaderResource u_time = {.type = GL_Type_Float, .name = "time"};
struct GL_ShaderResource u_model_matrix = {.type = GL_Type_Float4x4, .name = "model_matrix"};
struct GL_ShaderResource u_view_matrix = {.type = GL_Type_Float4x4, .name = "view_matrix"};
struct GL_ShaderResource u_model_view_matrix = {
    .type = GL_Type_Float4x4, .name = "model_view_matrix", .uniform.prepare = prepare_model_view};
struct GL_ShaderResource u_projection_matrix = {.type = GL_Type_Float4x4, .name = "projection_matrix"};
struct GL_ShaderResource u_view_projection_matrix = {
    .type = GL_Type_Float4x4, .name = "view_projection_matrix", .uniform.prepare = prepare_view_projection};
struct GL_ShaderResource u_model_view_projection_matrix = {
    .type = GL_Type_Float4x4, .name = "model_view_projection_matrix", .uniform.prepare = prepare_model_view_projection};

THIN_GL_SHADER(_ui_vertex,
  code(
    layout(location = 0) out vec2 out_st;
    layout(location = 1) out vec4 out_rgba;
  ),
  main(
    gl_Position = u_view_projection_matrix * vec4(in_xy, 0, 1);
    out_st = in_st;
    out_rgba = in_rgba;
  )
)

THIN_GL_SHADER(_ui_fragment,
  code(
    layout(location = 0) in vec2 in_st;
    layout(location = 1) in vec4 in_rgba;

    layout(location = 0) out vec4 out_color;
  ),
  main(
    out_color = texture(u_img, in_st) * in_rgba;
  )
)

static struct GL_Buffer _ui_element_buffer;
static struct GL_Buffer _ui_vertexes_buffer;
static struct GL_DrawState _ui_draw_state = {.primitive = GL_TRIANGLES,
                                         .attribute[0] = {0, alias_memory_Format_Float32, 2, "xy", 0},
                                         .attribute[1] = {0, alias_memory_Format_Float32, 4, "rgba", 8},
                                         .attribute[2] = {0, alias_memory_Format_Float32, 2, "st", 24},
                                         .binding[0] = {sizeof(struct BackendUIVertex)},
                                         .global[0] = {THIN_GL_VERTEX_BIT, &u_view_projection_matrix},
                                         .image[0] = {THIN_GL_FRAGMENT_BIT, GL_Type_Sampler2D, "img"},
                                         .vertex_shader = &_ui_vertex_shader,
                                         .fragment_shader = &_ui_fragment_shader,
                                         .depth_range_min = 0,
                                         .depth_range_max = 1,
                                         .blend_enable = true,
                                         .blend_src_factor = GL_SRC_ALPHA,
                                         .blend_dst_factor = GL_ONE_MINUS_SRC_ALPHA};

void BackendUIVertex_begin_draw(uint32_t num_indexes, uint32_t ** indexes_ptr, uint32_t num_vertexes, struct BackendUIVertex ** vertexes_ptr) {
  _ui_element_buffer = GL_allocate_temporary_buffer(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32_t) * num_indexes);
  _ui_vertexes_buffer = GL_allocate_temporary_buffer(GL_ARRAY_BUFFER, sizeof(struct BackendUIVertex) * num_vertexes);

  *indexes_ptr = _ui_element_buffer.mapping;
  *vertexes_ptr = _ui_vertexes_buffer.mapping;
}

void BackendUIVertex_draw(struct BackendImage * image, uint32_t index_offset, uint32_t num_indexes) {
  GL_draw_elements(&_ui_draw_state, &(struct GL_DrawAssets){
    .image[0] = image->gl.image,
    .element_buffer = &_ui_element_buffer,
    .element_buffer_offset = index_offset,
    .vertex_buffers[0] = &_ui_vertexes_buffer
  }, num_indexes, 1, 0, 0);
}

void BackendUIVertex_end_draw(void) {
}

static void _draw_rectangle_action(const struct alias_LocalToWorld2D *t, const struct DrawRectangle *r) {
 alias_R
      hw = r->width / 2
    , hh = r->height / 2
    , bl = -hw
    , br =  hw
    , bt = -hh
    , bb =  hh
    ;
  alias_pga2d_Point box[] = {
      alias_pga2d_point(br, bb)
    , alias_pga2d_point(br, bt)
    , alias_pga2d_point(bl, bt)
    , alias_pga2d_point(bl, bb)
    };
  box[0] = alias_pga2d_sandwich_bm(box[0], t->motor);
  box[1] = alias_pga2d_sandwich_bm(box[1], t->motor);
  box[2] = alias_pga2d_sandwich_bm(box[2], t->motor);
  box[3] = alias_pga2d_sandwich_bm(box[3], t->motor);
  uint32_t * indexes;
  struct BackendUIVertex * vertexes;
  BackendUIVertex_begin_draw(6, &indexes, 4, &vertexes);
  vertexes[0] = (struct BackendUIVertex) { .xy[0] = alias_pga2d_point_x(box[0]), .xy[1] = alias_pga2d_point_y(box[0]), .rgba = { r->color.r, r->color.g, r->color.b, r->color.a } };
  vertexes[1] = (struct BackendUIVertex) { .xy[0] = alias_pga2d_point_x(box[1]), .xy[1] = alias_pga2d_point_y(box[1]), .rgba = { r->color.r, r->color.g, r->color.b, r->color.a } };
  vertexes[2] = (struct BackendUIVertex) { .xy[0] = alias_pga2d_point_x(box[2]), .xy[1] = alias_pga2d_point_y(box[2]), .rgba = { r->color.r, r->color.g, r->color.b, r->color.a } };
  vertexes[3] = (struct BackendUIVertex) { .xy[0] = alias_pga2d_point_x(box[3]), .xy[1] = alias_pga2d_point_y(box[3]), .rgba = { r->color.r, r->color.g, r->color.b, r->color.a } };
  indexes[0] = 0;
  indexes[1] = 1;
  indexes[2] = 2;
  indexes[3] = 0;
  indexes[4] = 2;
  indexes[5] = 3;
  BackendUIVertex_draw(NULL, 0, 6);
  BackendUIVertex_end_draw();
}

QUERY(_draw_rectangles
  , read(alias_LocalToWorld2D, t)
  , read(DrawRectangle, r)
  , action(
    (void)state;
    _draw_rectangle_action(t, r);
  )
)

void replacement_DrawCircle(float x, float y, float radius, alias_Color color) {
#define NUM_CIRCLE_SEGMENTS 32

  struct BackendUIVertex * vertexes;
  uint32_t * indexes;

  BackendUIVertex_begin_draw(3 * (NUM_CIRCLE_SEGMENTS - 2), &indexes, NUM_CIRCLE_SEGMENTS, &vertexes);

  for(uint32_t i = 0; i < NUM_CIRCLE_SEGMENTS; i++) {
    alias_R angle = (alias_R)i / NUM_CIRCLE_SEGMENTS * alias_R_PI * 2;
    alias_R s = alias_R_sin(angle);
    alias_R c = alias_R_cos(angle);
    vertexes[i].xy[0] = x + s * radius;
    vertexes[i].xy[1] = y + c * radius;
    vertexes[i].rgba[0] = color.r;
    vertexes[i].rgba[1] = color.g;
    vertexes[i].rgba[2] = color.b;
    vertexes[i].rgba[3] = color.a;
    vertexes[i].st[0] = 0;
    vertexes[i].st[1] = 0;

    if(i >= 2) {
      indexes[(i - 2) * 3 + 0] = 0;
      indexes[(i - 2) * 3 + 1] = i - 1;
      indexes[(i - 2) * 3 + 2] = i - 0;
    }
  }

  BackendUIVertex_draw(NULL, 0, 3 * (NUM_CIRCLE_SEGMENTS - 2));
  BackendUIVertex_end_draw();

#undef NUM_CIRCLE_SEGMENTS
}

static struct FontGlyph BreeSerif_glyphs[] = {
   { 32,0.20000000000000001,0,0,0,0,0,0,0,0 }
 , { 33,0.27700000000000002,0.049734396671289864,-0.038253814147018048,0.22726560332871013,0.71625381414701805,0.5,178.5,8.5,212.5 }
 , { 34,0.34900000000000003,0.040851595006934839,0.41685159500693481,0.30714840499306523,0.68314840499306528,226.5,12.5,238.5,24.5 }
 , { 35,0.53400000000000003,0.0042988904299583871,-0.019275312066574173,0.51470110957004156,0.62427531206657416,203.5,52.5,226.5,81.5 }
 , { 36,0.53500000000000003,0.03749029126213594,-0.10473231622746189,0.50350970873786416,0.76073231622746185,127.5,216.5,148.5,255.5 }
 , { 37,0.80100000000000005,0.013150485436893244,-0.033562413314840507,0.78984951456310692,0.67656241331484057,173.5,146.5,208.5,178.5 }
 , { 38,0.71399999999999997,0.034128987517337055,-0.055753814147018056,0.69987101248266315,0.69875381414701809,8.5,178.5,38.5,212.5 }
 , { 39,0.20500000000000002,0.035925797503467395,0.41685159500693481,0.16907420249653257,0.68314840499306528,249.5,200.5,255.5,212.5 }
 , { 40,0.33100000000000002,0.049351595006934812,-0.18951941747572815,0.31564840499306518,0.74251941747572825,15.5,213.5,27.5,255.5 }
 , { 41,0.33100000000000002,0.015351595006934809,-0.18751941747572815,0.2816484049930652,0.74451941747572825,40.5,213.5,52.5,255.5 }
 , { 42,0.45200000000000001,0.025777392510402224,0.39387309292649098,0.42522260748959784,0.77112690707350906,188.5,7.5,206.5,24.5 }
 , { 43,0.53500000000000003,0.045085991678224697,0.09358599167822472,0.48891400832177534,0.53741400832177533,146.5,4.5,166.5,24.5 }
 , { 44,0.24099999999999999,0.01913869625520109,-0.17433980582524275,0.2188613037447989,0.13633980582524272,244.5,118.5,253.5,132.5 }
 , { 45,0.32200000000000001,0.016755894590846056,0.22142579750346741,0.30524410540915398,0.35457420249653265,232.5,218.5,245.5,224.5 }
 , { 46,0.22600000000000001,0.024234396671289866,-0.032265603328710125,0.20176560332871013,0.14526560332871011,224.5,216.5,232.5,224.5 }
 , { 47,0.42799999999999999,-0.0089140083217753002,-0.12132801664355065,0.43491400832177535,0.76632801664355066,87.5,215.5,107.5,255.5 }
 , { 48,0.53500000000000003,0.02289459084604718,-0.031562413314840478,0.51110540915395286,0.67856241331484057,208.5,146.5,230.5,178.5 }
 , { 49,0.45400000000000001,0.025181692094313476,-0.021966712898751725,0.44681830790568661,0.66596671289875176,194.5,113.5,213.5,144.5 }
 , { 50,0.51300000000000001,0.047681692094313478,-0.015466712898751732,0.46931830790568663,0.67246671289875182,211.5,81.5,230.5,112.5 }
 , { 51,0.52200000000000002,0.017990291262135919,-0.033062413314840507,0.48400970873786414,0.67706241331484063,230.5,146.5,251.5,178.5 }
 , { 52,0.53600000000000003,0.028894590846047188,-0.014466712898751725,0.51710540915395287,0.67346671289875182,67.5,50.5,89.5,81.5 }
 , { 53,0.50700000000000001,0.015585991678224715,-0.025253814147018012,0.45941400832177531,0.72925381414701806,98.5,178.5,118.5,212.5 }
 , { 54,0.53400000000000003,0.038490291262135955,-0.025062413314840479,0.50450970873786416,0.68506241331484063,0.5,112.5,21.5,144.5 }
 , { 55,0.47500000000000003,0.0059902912621359475,-0.021966712898751725,0.47200970873786419,0.66596671289875176,120.5,50.5,141.5,81.5 }
 , { 56,0.53500000000000003,0.035490291262135931,-0.032062413314840471,0.50150970873786416,0.67806241331484063,21.5,112.5,42.5,144.5 }
 , { 57,0.53500000000000003,0.033990291262135951,-0.032562413314840506,0.50000970873786421,0.67756241331484057,71.5,112.5,92.5,144.5 }
 , { 58,0.22600000000000001,0.024234396671289866,-0.036892510402219109,0.20176560332871013,0.51789251040221929,168.5,25.5,176.5,50.5 }
 , { 59,0.24099999999999999,0.014042995839112338,-0.17346671289875171,0.23595700416088766,0.51446671289875179,239.5,181.5,249.5,212.5 }
 , { 60,0.53500000000000003,0.050085991678224695,0.085490291262135934,0.49391400832177534,0.55150970873786409,106.5,3.5,126.5,24.5 }
 , { 61,0.53500000000000003,0.045585991678224684,0.15066019417475729,0.48941400832177534,0.4613398058252427,206.5,10.5,226.5,24.5 }
 , { 62,0.53500000000000003,0.050085991678224695,0.085490291262135934,0.49391400832177534,0.55150970873786409,126.5,3.5,146.5,24.5 }
 , { 63,0.433,0.018777392510402252,-0.038253814147018048,0.41822260748959783,0.71625381414701805,197.5,178.5,215.5,212.5 }
 , { 64,0.95300000000000007,0.05436338418862692,-0.20523231622746188,0.89763661581137311,0.66023231622746192,154.5,216.5,192.5,255.5 }
 , { 65,0.66200000000000003,-0.012966712898751703,-0.011466712898751711,0.67496671289875176,0.67646671289875182,224.5,224.5,255.5,255.5 }
 , { 66,0.60199999999999998,0.014011789181692108,-0.011466712898751711,0.59098821081830799,0.67646671289875182,0.5,50.5,26.5,81.5 }
 , { 67,0.60199999999999998,0.018607489597780892,-0.034158113730929258,0.57339251040221928,0.69815811373092929,71.5,145.5,96.5,178.5 }
 , { 68,0.66500000000000004,0.018820388349514553,-0.011466712898751711,0.64017961165048543,0.67646671289875182,183.5,81.5,211.5,112.5 }
 , { 69,0.54900000000000004,0.016298890429958406,-0.011466712898751711,0.52670110957004157,0.67646671289875182,160.5,81.5,183.5,112.5 }
 , { 70,0.54300000000000004,0.022798890429958397,-0.011466712898751711,0.53320110957004163,0.67646671289875182,137.5,81.5,160.5,112.5 }
 , { 71,0.64100000000000001,0.028416088765603319,-0.033658113730929265,0.62758391123439672,0.69865811373092923,96.5,145.5,123.5,178.5 }
 , { 72,0.71199999999999997,0.014033287101248293,-0.011466712898751711,0.70196671289875179,0.67646671289875182,69.5,81.5,100.5,112.5 }
 , { 73,0.307,0.011255894590846057,-0.011466712898751711,0.29974410540915397,0.67646671289875182,56.5,81.5,69.5,112.5 }
 , { 74,0.47400000000000003,0.010490291262135942,-0.029062413314840486,0.47650970873786413,0.68106241331484063,92.5,112.5,113.5,144.5 }
 , { 75,0.622,0.011320388349514564,-0.011466712898751711,0.63267961165048547,0.67646671289875182,0.5,81.5,28.5,112.5 }
 , { 76,0.53700000000000003,0.014298890429958404,-0.011466712898751711,0.52470110957004157,0.67646671289875182,163.5,50.5,186.5,81.5 }
 , { 77,0.83399999999999996,0.0064590846047157041,-0.011466712898751711,0.8275409153952844,0.67646671289875182,100.5,81.5,137.5,112.5 }
 , { 78,0.70499999999999996,0.012033287101248282,-0.011466712898751711,0.69996671289875179,0.67646671289875182,213.5,113.5,244.5,144.5 }
 , { 79,0.66400000000000003,0.021320388349514573,-0.034158113730929258,0.64267961165048537,0.69815811373092929,123.5,145.5,151.5,178.5 }
 , { 80,0.58999999999999997,0.018607489597780892,-0.011466712898751711,0.57339251040221928,0.67646671289875182,169.5,113.5,194.5,144.5 }
 , { 81,0.66400000000000003,0.017437586685159524,-0.17723231622746186,0.72756241331484062,0.68823231622746184,192.5,216.5,224.5,255.5 }
 , { 82,0.626,0.013320388349514576,-0.011466712898751711,0.63467961165048548,0.67646671289875182,28.5,81.5,56.5,112.5 }
 , { 83,0.52200000000000002,0.022394590846047179,-0.034158113730929258,0.51060540915395292,0.69815811373092929,151.5,145.5,173.5,178.5 }
 , { 84,0.55300000000000005,-0.00089251040221912173,-0.011466712898751711,0.55389251040221932,0.67646671289875182,230.5,81.5,255.5,112.5 }
 , { 85,0.65900000000000003,0.009724687933425822,-0.029062413314840486,0.65327531206657419,0.68106241331484063,42.5,112.5,71.5,144.5 }
 , { 86,0.65400000000000003,-0.016966712898751724,-0.011466712898751711,0.67096671289875176,0.67646671289875182,89.5,50.5,120.5,81.5 }
 , { 87,0.88500000000000001,-0.012423717059639388,-0.011466712898751711,0.89742371705963941,0.67646671289875182,26.5,50.5,67.5,81.5 }
 , { 88,0.621,-0.011275312066574197,-0.011466712898751711,0.63227531206657417,0.67646671289875182,140.5,113.5,169.5,144.5 }
 , { 89,0.57000000000000006,-0.014583911234396675,-0.011466712898751711,0.58458391123439679,0.67646671289875182,113.5,113.5,140.5,144.5 }
 , { 90,0.53300000000000003,0.018894590846047173,-0.011466712898751711,0.50710540915395297,0.67646671289875182,141.5,50.5,163.5,81.5 }
 , { 91,0.32500000000000001,0.062947295423023589,-0.18092371705963939,0.30705270457697648,0.72892371705963943,65.5,214.5,76.5,255.5 }
 , { 92,0.44,-0.0019140083217753024,-0.12032801664355065,0.44191400832177535,0.76732801664355066,107.5,215.5,127.5,255.5 }
 , { 93,0.32500000000000001,0.017947295423023576,-0.18092371705963939,0.26205270457697638,0.72892371705963943,76.5,214.5,87.5,255.5 }
 , { 94,0.55300000000000005,0.034894590846047173,0.26868169209431347,0.52310540915395287,0.6903183079056866,166.5,5.5,188.5,24.5 }
 , { 95,0.5,-0.016296809986130371,-0.16597850208044385,0.51629680998613037,-0.05502149791955617,196.5,36.5,220.5,41.5 }
 , { 96,0.5,0.14304299583911237,0.54035159500693486,0.36495700416088767,0.80664840499306523,244.5,132.5,254.5,144.5 }
 , { 97,0.56700000000000006,0.023203190013869623,-0.02489251040221914,0.55579680998613035,0.52989251040221919,144.5,25.5,168.5,50.5 }
 , { 98,0.55600000000000005,0.0012031900138696284,-0.027253814147018014,0.53379680998613033,0.72725381414701806,0.5,144.5,24.5,178.5 }
 , { 99,0.47100000000000003,0.01508599167822469,-0.025392510402219137,0.45891400832177531,0.52939251040221913,86.5,25.5,106.5,50.5 }
 , { 100,0.56800000000000006,0.025203190013869631,-0.027253814147018014,0.55779680998613035,0.72725381414701806,215.5,178.5,239.5,212.5 }
 , { 101,0.48399999999999999,0.0210859916782247,-0.02489251040221914,0.46491400832177532,0.52989251040221919,176.5,25.5,196.5,50.5 }
 , { 102,0.37,-0.0018183079056865274,-0.015753814147018021,0.41981830790568658,0.73875381414701802,178.5,178.5,197.5,212.5 }
 , { 103,0.54400000000000004,0.013394590846047175,-0.22525381414701801,0.50160540915395291,0.52925381414701811,156.5,178.5,178.5,212.5 }
 , { 104,0.59999999999999998,0.014511789181692119,-0.020753814147018042,0.59148821081830794,0.73375381414701801,130.5,178.5,156.5,212.5 }
 , { 105,0.29599999999999999,0.018851595006934823,-0.018753814147018034,0.28514840499306526,0.73575381414701801,118.5,178.5,130.5,212.5 }
 , { 106,0.28300000000000003,-0.099435506241331503,-0.22561511789181693,0.23343550624133147,0.72861511789181688,0.5,212.5,15.5,255.5 }
 , { 107,0.55600000000000005,0.020703190013869645,-0.020753814147018042,0.5532968099861304,0.73375381414701801,74.5,178.5,98.5,212.5 }
 , { 108,0.29799999999999999,0.016851595006934821,-0.020753814147018042,0.28314840499306526,0.73375381414701801,62.5,178.5,74.5,212.5 }
 , { 109,0.86599999999999999,0.011863384188626909,-0.01839251040221913,0.85513661581137312,0.53639251040221914,106.5,25.5,144.5,50.5 }
 , { 110,0.59999999999999998,0.012011789181692096,-0.01839251040221913,0.58898821081830799,0.53639251040221914,226.5,56.5,252.5,81.5 }
 , { 111,0.52800000000000002,0.019394590846047173,-0.036988210818307901,0.50760540915395291,0.53998821081830795,0.5,24.5,22.5,50.5 }
 , { 112,0.56800000000000006,0.0097031900138696454,-0.221753814147018,0.54229680998613039,0.53275381414701817,38.5,178.5,62.5,212.5 }
 , { 113,0.55100000000000005,0.024203190013869627,-0.221753814147018,0.55679680998613035,0.53275381414701817,24.5,144.5,48.5,178.5 }
 , { 114,0.46200000000000002,0.0095859916782246885,-0.01839251040221913,0.45341400832177536,0.53639251040221914,66.5,25.5,86.5,50.5 }
 , { 115,0.45100000000000001,0.032777392510402223,-0.02489251040221914,0.43222260748959779,0.52989251040221919,48.5,25.5,66.5,50.5 }
 , { 116,0.377,0.006873092926491008,-0.035371012482662972,0.38412690707350911,0.63037101248266303,186.5,51.5,203.5,81.5 }
 , { 117,0.59099999999999997,0.0065117891816921054,-0.032392510402219139,0.58348821081830793,0.52239251040221912,22.5,25.5,48.5,50.5 }
 , { 118,0.55800000000000005,-0.0094882108183078854,-0.014796809986130368,0.56748821081830791,0.51779680998613042,20.5,0.5,46.5,24.5 }
 , { 119,0.79500000000000004,-0.0019452149791955779,-0.014796809986130368,0.79694521497919546,0.51779680998613042,46.5,0.5,82.5,24.5 }
 , { 120,0.53400000000000003,-0.00129680998613037,-0.014796809986130368,0.53129680998613038,0.51779680998613042,82.5,0.5,106.5,24.5 }
 , { 121,0.57300000000000006,0.0097988904299584085,-0.23275381414701804,0.52020110957004162,0.52175381414701805,48.5,144.5,71.5,178.5 }
 , { 122,0.48299999999999998,0.022085991678224687,-0.014796809986130368,0.46591400832177532,0.51779680998613042,0.5,0.5,20.5,24.5 }
 , { 123,0.34000000000000002,0.029755894590846054,-0.19201941747572812,0.31824410540915399,0.7400194174757283,52.5,213.5,65.5,255.5 }
 , { 124,0.27400000000000002,0.070425797503467405,-0.10323231622746189,0.2035742024965326,0.7622323162274619,148.5,216.5,154.5,255.5 }
 , { 125,0.34000000000000002,0.021755894590846054,-0.19201941747572812,0.31024410540915398,0.7400194174757283,27.5,213.5,40.5,255.5 }
 , { 126,0.59799999999999998,0.032703190013869635,0.20313869625520112,0.56529680998613041,0.40286130374479895,196.5,41.5,220.5,50.5 }
};

struct Font BreeSerif = {
    .atlas      = { .path = "fonts/breeserif_soft.png" }
  , .atlas_type = FontAtlasType_Bitmap
  , .num_glyphs = sizeof(BreeSerif_glyphs) / sizeof(BreeSerif_glyphs[0])
  , .glyphs     = BreeSerif_glyphs
};

void replacement_DrawText(const char * text, float x, float y, float size, alias_Color color) {
  Font_draw(&BreeSerif, text, x, y, size, 0, color);
}

void replacement_MeasureTextEx(const char * text, float size, float spacing, float * width, float * height) {
  Font_measure(&BreeSerif, text, size, spacing, width, height);
}

QUERY(_draw_circles
  , read(alias_LocalToWorld2D, transform)
  , read(DrawCircle, c)
  , action(
    (void)state;
    replacement_DrawCircle(alias_pga2d_point_x(transform->position), alias_pga2d_point_y(transform->position), c->radius, c->color);
  )
)

QUERY(_draw_text
  , read(alias_LocalToWorld2D, w)
  , read(DrawText, t)
  , action(
    (void)state;
    replacement_DrawText(t->text, alias_pga2d_point_x(w->position), alias_pga2d_point_y(w->position), t->size, t->color);
  )
)

static void _draw_sprite(const struct alias_LocalToWorld2D *t, const struct Sprite *s) {
  struct LoadedResource *res = Engine__load_image(s->image);

  alias_R hw = res->image.width / 2, hh = res->image.height / 2, bl = -hw, br = hw, bt = -hh, bb = hh;

  alias_pga2d_Point box[] = {alias_pga2d_point(br, bb), alias_pga2d_point(br, bt), alias_pga2d_point(bl, bt),
                             alias_pga2d_point(bl, bb)};

  box[0] = alias_pga2d_sandwich_bm(box[0], t->motor);
  box[1] = alias_pga2d_sandwich_bm(box[1], t->motor);
  box[2] = alias_pga2d_sandwich_bm(box[2], t->motor);
  box[3] = alias_pga2d_sandwich_bm(box[3], t->motor);

  uint32_t *indexes;
  struct BackendUIVertex *vertexes;
  BackendUIVertex_begin_draw(6, &indexes, 4, &vertexes);

  vertexes[0] = (struct BackendUIVertex){.xy = {alias_pga2d_point_x(box[0]), alias_pga2d_point_y(box[0])},
                                         .rgba = {s->color.r, s->color.g, s->color.b, s->color.a},
                                         .st = {s->s1, s->t1}};
  vertexes[1] = (struct BackendUIVertex){.xy = {alias_pga2d_point_x(box[1]), alias_pga2d_point_y(box[1])},
                                         .rgba = {s->color.r, s->color.g, s->color.b, s->color.a},
                                         .st = {s->s1, s->t0}};
  vertexes[2] = (struct BackendUIVertex){.xy = {alias_pga2d_point_x(box[2]), alias_pga2d_point_y(box[2])},
                                         .rgba = {s->color.r, s->color.g, s->color.b, s->color.a},
                                         .st = {s->s0, s->t0}};
  vertexes[3] = (struct BackendUIVertex){.xy = {alias_pga2d_point_x(box[3]), alias_pga2d_point_y(box[3])},
                                         .rgba = {s->color.r, s->color.g, s->color.b, s->color.a},
                                         .st = {s->s0, s->t1}};

  indexes[0] = 0;
  indexes[1] = 1;
  indexes[2] = 2;
  indexes[3] = 0;
  indexes[4] = 2;
  indexes[5] = 3;

  BackendUIVertex_draw(&res->image, 0, 6);
  BackendUIVertex_end_draw();
}

QUERY(_draw_sprites
  , read(alias_LocalToWorld2D, t)
  , read(Sprite, s)
  , action(
    (void)state;
    _draw_sprite(t, s);
  )
)

static void _update_display_action(const struct alias_LocalToWorld2D * transform, const struct Camera * camera) {
  int x = alias_pga2d_point_x(camera->viewport_min) * run_render_width;
  int y = alias_pga2d_point_y(camera->viewport_min) * run_render_height;
  int width = (alias_pga2d_point_x(camera->viewport_max) * run_render_width) - x;
  int height = (alias_pga2d_point_y(camera->viewport_max) * run_render_height) - y;

  glViewport(x, y, width, height);

  alias_pga2d_Point center = alias_pga2d_sandwich_bm(alias_pga2d_point(0, 0), transform->motor);

  float offset_x = width / 2.0f;
  float offset_y = height / 2.0f;
  float target_x = alias_pga2d_point_x(center);
  float target_y = alias_pga2d_point_y(center);
  float rotation = 0;
  float transform_m[16];

  {
    float origin_m[16], rotation_m[16], scale_m[16], translation_m[16];
    GL_matrix_translation(-target_x, -target_y, 0, origin_m);
    GL_matrix_rotation_z(rotation * alias_R_PI / 180.0f, rotation_m);
    GL_matrix_scale(camera->zoom, camera->zoom, 1, scale_m);
    GL_matrix_translation(offset_x, offset_y, 0, translation_m);
    GL_matrix_multiply(scale_m, rotation_m, transform_m);
    GL_matrix_multiply(origin_m, transform_m, transform_m);
    GL_matrix_multiply(transform_m, translation_m, transform_m);
  }

  glClearColor(0, 0, 0, 1);
  glClear(GL_COLOR_BUFFER_BIT);

  _draw_sprites();
  _draw_rectangles();
  _draw_circles();
  _draw_text();
}

QUERY(_update_display
  , read(alias_LocalToWorld2D, transform)
  , read(Camera, camera)
  , pre(
    glfwGetFramebufferSize(run_glfw_window, &run_render_width, &run_render_height);
    glClearColor(0.9, 0.9, 0.9, 1);
    glClear(GL_COLOR_BUFFER_BIT);

    GL_reset_temporary_buffers();
  )
  , action(
    (void)state;
    _update_display_action(transform, camera);
  )
  , post(
    GL_matrix_ortho(0, run_render_width, run_render_height, 0, -99999, 99999, u_projection_matrix.uniform.data.mat);
    GL_matrix_identity(u_view_matrix.uniform.data.mat);
    glViewport(0, 0, run_render_width, run_render_height);

    Engine__update_ui();

    glfwSwapBuffers(run_glfw_window);
  )
)