#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>

#ifdef PLATFORM_WINDOWS
  #define SOKOL_D3D11
#elifdef PLATFORM_MACOS
  #define SOKOL_METAL
#elifdef PLATFORM_LINUX
  #define SOKOL_GLCORE
#elifdef PLATFORM_WASM
  #define SOKOL_GLES3
#else
  #error "unsupported platform"
#endif

#define SOKOL_IMPL
#include "sokol/sokol_app.h"
#include "sokol/sokol_gfx.h"
#include "sokol/sokol_glue.h"
#include "sokol/sokol_log.h"
#include "sokol/util/sokol_debugtext.h"

#include "base.c"
#include "arena.c"
#include "math.c"

#include "basic.glsl.h"

typedef struct {
  Matrix model;
  Matrix view;
  Matrix proj;
} UBO;


sg_pass_action pass_action;
sg_bindings g_bind;
sg_pipeline g_pipeline;

s64 g_frame = 0;
UBO g_ubo;
Matrix g_proj;

Vec3 g_camera;

static void
init(void) {
  scope_exit {
    printf("leaving init...\n");
  };

  sg_setup(&(sg_desc){
    .environment = sglue_environment(),
    .logger.func = slog_func,
  });

  g_proj = make_proj_matrix(90, 1280, 720, 0.01, 100.0);
  g_camera = (Vec3){0, 0, -1.0};

  float vertices[] = {
    -0.5,  0.5, -0.5,  1.0, 1.0, 1.0, 1.0,
    -0.5, -0.5, -0.5,  1.0, 0.0, 1.0, 1.0,
     0.5,  0.5, -0.5,  0.0, 0.0, 1.0, 1.0,
     0.5, -0.5, -0.5,  1.0, 0.0, 1.0, 1.0,

    -0.5,  0.5, 0.5,  1.0, 0.0, 0.0, 1.0,
    -0.5, -0.5, 0.5,  1.0, 1.0, 0.0, 1.0,
     0.5,  0.5, 0.5,  0.0, 0.0, 0.0, 1.0,
     0.5, -0.5, 0.5,  0.0, 1.0, 0.0, 1.0
  };

  u32 indices[] = {
    0, 2, 1, 1, 2, 3, // back
    0, 1, 4, 4, 1, 5, // left
    3, 2, 6, 6, 7, 3, // right
    2, 0, 6, 6, 0, 4, // top
    1, 3, 7, 7, 5, 1, // bot
    4, 5, 6, 6, 5, 7, // front
  };

  g_bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
    .usage = {.dynamic_update = true},
    .size = 1024,
    .label = "cube vertices",
  });
  g_bind.index_buffer = sg_make_buffer(&(sg_buffer_desc){
    .data = SG_RANGE(indices),
    .usage = {.index_buffer = true},
    .label = "cube indices",
  });

  sg_shader shader = sg_make_shader(basic_shader_desc(sg_query_backend()));

  g_pipeline = sg_make_pipeline(&(sg_pipeline_desc){
    .shader = shader,
    .layout = {
      .attrs = {
        [ATTR_basic_pos].format    = SG_VERTEXFORMAT_FLOAT3,
        [ATTR_basic_vcolor].format = SG_VERTEXFORMAT_FLOAT4,
      },
    },
    .cull_mode =  SG_CULLMODE_BACK,
    .index_type = SG_INDEXTYPE_UINT32,
    .label = "cube pipeline",
  });

  pass_action = (sg_pass_action) {
    .colors[0] = {
      .load_action = SG_LOADACTION_CLEAR,
      .clear_value = { 1.0f, 0.0f, 0.0f, 1.0f }
    }
  };

  sdtx_setup(&(sdtx_desc_t){
    .fonts = {
      [0] = sdtx_font_kc853(),
      [1] = sdtx_font_kc854(),
      [2] = sdtx_font_z1013(),
      [3] = sdtx_font_cpc(),
      [4] = sdtx_font_c64(),
      [5] = sdtx_font_oric(),
    },
  });
}

static void
frame(void) {
  sg_begin_pass(&(sg_pass){
    .action.colors[0] = {
      .load_action = SG_LOADACTION_CLEAR,
      .clear_value = { 0.10f, 0.10f, 0.10f, 1.0f },
    },
    .swapchain = sglue_swapchain(),
  });

  sdtx_origin(0, 0);
  sdtx_canvas(sapp_widthf(), sapp_heightf());

  sdtx_font(3);
  sdtx_pos(1, 1);
  sdtx_color3b(0, 0xFF, 0);
  sdtx_puts("press escape to quit");
  sdtx_draw();

  float vertices[] = {
    -0.5,  0.5, -0.5,  1.0, 1.0, 1.0, 1.0,
    -0.5, -0.5, -0.5,  1.0, 0.0, 1.0, 1.0,
     0.5,  0.5, -0.5,  0.0, 1.0, 1.0, 1.0,
     0.5, -0.5, -0.5,  0.0, 0.0, 1.0, 1.0,

    -0.5,  0.5, 0.5,  1.0, 1.0, 0.0, 1.0,
    -0.5, -0.5, 0.5,  1.0, 0.0, 0.0, 1.0,
     0.5,  0.5, 0.5,  0.0, 1.0, 0.0, 1.0,
     0.5, -0.5, 0.5,  0.0, 0.0, 0.0, 1.0
  };

  // get angle from frame number
  // wrap to angle
  s64 rate = 333;
  s64 wframe = g_frame;
  if(wframe >= TAU32*rate)
    wframe -= TAU32*rate;
  float angle = (f32)wframe/rate;

  Matrix rot = make_rotation((Vec3){0.0, 1.0, 0.0}, angle);
  Vec3 p;
  for(int i = 0;
     i < 8;
     ++i) {
    int vertex = i*7;
    p = (Vec3){vertices[vertex + 0], vertices[vertex + 1], vertices[vertex + 2]};
    p = m4v3_mul(rot, p);
    vertices[vertex + 0] = p.x;
    vertices[vertex + 1] = p.y;
    vertices[vertex + 2] = p.z;
  }

  sg_update_buffer(g_bind.vertex_buffers[0], &SG_RANGE(vertices));

  sg_apply_pipeline(g_pipeline);
  sg_apply_bindings(&g_bind);

  Matrix model = make_translation((Vec3){0, 0, 0});
  Matrix lookat = make_lookat(g_camera, (Vec3){0, 0, 0}, (Vec3){0, 1, 0});

  g_ubo.model = model;
  g_ubo.view = lookat;
  g_ubo.proj = g_proj;
  sg_apply_uniforms(UB_UBO, &SG_RANGE(g_ubo));

  sg_draw(0, 36, 1);

  sg_end_pass();
  sg_commit();

  g_frame += 1;
}

static void
cleanup(void) {
  sdtx_shutdown();
  sg_shutdown();
}

static void
event(const sapp_event* event) {
  if (event->type == SAPP_EVENTTYPE_KEY_DOWN) {
    if (event->key_code == SAPP_KEYCODE_ESCAPE) {
      sapp_request_quit();
    }
  }
}

sapp_desc
sokol_main(int argc, char* argv[]) {
  return (sapp_desc) {
    .init_cb     = init,
    .frame_cb    = frame,
    .cleanup_cb  = cleanup,
    .event_cb    = event,
    .logger.func = slog_func,

    .width  = 1280,
    .height = 720,
    .window_title = "hello, world",
  };
}
