#include "HandmadeMath/HandmadeMath.h"
#include "gear/base.h"
#include "gear/graphics/pipeline.h"
#include "gear/images.h"
#define SL_IMPLEMENTATION
#include "slibs/slibs.h"
#include <gear/graphics/renderer.h>
#include <gear/camera.h>
#include <gear/ui/gui.h>
#include <gear/ui/console.h>
#include <gear/resources.h>
#include <gear/graphics/models.h>
#include <shaders/transform.glsl.h>

#include <sokol/sokol_app.h>
#include <sokol/sokol_gfx.h>
#include <sokol/sokol_log.h>
#include <sokol/sokol_glue.h>

#include <stb/stb_image.h>

#include <log.c/log.h>

#include <gear/graphics/textures.h>

g_renderer* renderer;
g_camera* camera;
g_gui* gui;
g_model* model;
sg_pipeline pipeline;
sg_bindings* bind;
sg_attachments attachments;

void init() {
    sg_setup(&(sg_desc){
        .environment = sglue_environment(),
        .logger.func = slog_func,
    });
    log_info("Sokol graphics initialized successfully");

    renderer = g_renderer_create(G_CYAN);
    gui = g_gui_create();
    camera = g_camera_create(HMM_V3(0.0f, 0.0f, -3.0f), HMM_V3(0.0f, 0.0f, 0.0f), 70.0f);
    log_info("GUI Layers (%d):", gui->layers.size);
    for(sl_vec_it(layer, gui->layers)) {
        log_info("    %s", (*layer)->name);
    }

    g_vertex vertices[] = {
        {  1.0f,  1.0f, 0.0f, G_WHITE, 1.0f * 32767, 1.0f * 32767 },   // top right
        {  1.0f, -1.0f, 0.0f, G_WHITE, 1.0f * 32767, 0.0f * 32767 },  // bottom right
        { -1.0f, -1.0f, 0.0f, G_WHITE, 0.0f * 32767, 0.0f * 32767 },  // bottom left
        { -1.0f,  1.0f, 0.0f, G_WHITE, 0.0f * 32767, 1.0f * 32767 }  // top left 
    };

    uint32_t indices[] = {
        0, 1, 3,
        1, 2, 3
    };

    g_vertex_vec vertices_vec = { 0 };
    g_index_vec indices_vec = { 0 };
    sl_vec_from_arr(vertices_vec, vertices);
    sl_vec_from_arr(indices_vec, indices);

    model = g_model_create(&vertices_vec, &indices_vec);

    bind = calloc(1, sizeof(sg_bindings));
    bind->vertex_buffers[0] = model->vertex_buffer;
    bind->index_buffer = model->index_buffer;

    g_texture* texture = g_texture_create_filtered(g_image_default(), SG_FILTER_NEAREST, SG_FILTER_NEAREST);
    bind->fs.images[SLOT_tex] = texture->image;
    bind->fs.samplers[SLOT_smp] = texture->sampler;

    sg_shader shd = sg_make_shader(transform_shader_desc(sg_query_backend()));

    pipeline = g_pipeline_create(shd, (sg_vertex_layout_state){
        .attrs = {
            [ATTR_vs_pos].format = SG_VERTEXFORMAT_FLOAT3,
            [ATTR_vs_color0].format = SG_VERTEXFORMAT_UBYTE4N,
            [ATTR_vs_texcoord0].format = SG_VERTEXFORMAT_SHORT2N,
        }
    });
}

void frame() {
    g_gui_update(gui);

    g_renderer_begin(renderer);
    g_pipeline_bind(renderer, pipeline);

    vs_params_t vs_params = {
        .mvp = HMM_Mul(g_camera_matrix(camera), HMM_Rotate_LH(HMM_AngleDeg(45.0f), HMM_V3(1.0f, 0.0f, 0.0f))),
        .texture_scale = HMM_V2(1.0f, 1.0f)
    };

    sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_vs_params, SG_RANGE_REF(vs_params));

    g_renderer_draw(renderer, (g_renderer_object){
        .pipeline = pipeline,
        .bind = bind
    }, model->indices.size);
    g_gui_render(sapp_width(), sapp_height());
    g_renderer_end(renderer);
}

void cleanup() {
    log_info("Shutting down");

    g_renderer_destroy(renderer);
    g_gui_destroy(gui);
    g_camera_destroy(camera);

    sg_shutdown();
}

void input(const sapp_event* event) {
    snk_handle_event(event);
}

sapp_desc sokol_main(int argc, char* argv[]) {
    (void)argc; (void)argv;
    FILE* log_fd = fopen("log.txt", "w");
    log_add_fp(log_fd, LOG_TRACE);

    log_info("Command line arguments (%d):", argc);
    for(i32 i = 0; i < argc; i++) {
        log_info("    %s", argv[i]);
    }

    return (sapp_desc){
        .init_cb = init,
        .frame_cb = frame,
        .cleanup_cb = cleanup,
        .event_cb = input,
        .width = 640,
        .height = 480,
        .window_title = "Gear",
        .icon.sokol_default = true,
        .logger.func = slog_func,
        .swap_interval = 0
    };
}
