#import "Basic";
using SDL3 :: #import "SDL3";
#import "GL";
#import "Math";
#import "Print_Vars";
ImGui :: #import "ImGui-docking";
#import "String";
#import "Compiler";

#load "imgui_impl_opengl3.jai";
#load "imgui_impl_sdl3.jai";

GLvoid :: void;

windowwidth  : s32 = 1280;
windowheight : s32 = 720;
window : *SDL_Window;
running : bool = true;

vs : string = #string DONE
#version 460 core

layout (location = 0) in vec2 datapos;
layout (location = 1) in vec4 datacolor;

uniform mat4 proj;
uniform mat4 model;

out vec4 intermediatecolor;

void main() {
    gl_Position = proj * model * vec4(datapos, 0.0, 1.0);
    intermediatecolor = datacolor;
}
DONE

fs : string = #string DONE
#version 460 core

in vec4 intermediatecolor;

out vec4 color;

void main() {
    color = intermediatecolor;
}
DONE

pos : Vector2;

offset_of :: ($T: Type, ident: Code) -> s64 #expand {
    t: T = ---;
    return cast(*void) (*t.#insert ident) - cast(*void) *t;
}

main :: () {
    SDL_Init(SDL_INIT_VIDEO);
    window = SDL_CreateWindow("Test", windowwidth, windowheight, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, xx SDL_GL_CONTEXT_PROFILE_CORE);

    glcontext := SDL_GL_CreateContext(window);

    gl_load(*gl, SDL_GL_GetProcAddress);

    print ("GL Vendor = %\n", to_string(glGetString(GL_VENDOR)));
    print ("GL Version = %\n", to_string(glGetString(GL_VERSION)));

    glClipControl(GL_UPPER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
    glEnable(GL_CULL_FACE);

    vshader : GLuint = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vshader, 1, *vs.data, cast(*s32) *vs.count);
    // vertex_shader_source is a GLchar* containing glsl shader source code
    glCompileShader(vshader);

    vertex_compiled : GLint;
    glGetShaderiv(vshader, GL_COMPILE_STATUS, *vertex_compiled);
    if (cast(bool) vertex_compiled != GL_TRUE) {
        auto_release_temp();

        length : s32;
        glGetShaderiv(vshader, GL_INFO_LOG_LENGTH, *length);

        info : string = alloc_string(length,, temp);
        glGetShaderInfoLog(vshader, xx info.count, null, info.data);

        log("[Error] Vertex Shader compilation failed in shader %", info);
    }

    fshader : GLuint = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fshader, 1, *fs.data, cast(*s32) *fs.count);
    // fragment_shader_source is a GLchar* containing glsl shader source code
    glCompileShader(fshader);

    fragment_compiled : GLint;
    glGetShaderiv(fshader, GL_COMPILE_STATUS, *fragment_compiled);
    if (cast(bool) fragment_compiled != GL_TRUE) {
        auto_release_temp();

        length : s32;
        glGetShaderiv(fshader, GL_INFO_LOG_LENGTH, *length);

        info : string = alloc_string(length,, temp);
        glGetShaderInfoLog(fshader, xx info.count, null, info.data);

        log("[Error] Fragment Shader compilation failed in shader %", info);
    }

    program : GLuint = glCreateProgram();

    glAttachShader(program, vshader);
    glAttachShader(program, fshader);
    glLinkProgram(program);

    program_linked : GLint;
    glGetProgramiv(program, GL_LINK_STATUS, *program_linked);
    if (cast(bool) program_linked != GL_TRUE) {
        auto_release_temp();

        length : s32;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, *length);

        info : string = alloc_string(length,, temp);
        glGetProgramInfoLog(program, xx info.count, null, info.data);

        log("[Error] Shader Program linking failed in shader %", info);
    }

    positions : [3]Vector2 = .[
        .{ 0.0,   100.0},
        .{ 50.0,  0.0},
        .{ 100.0, 100.0},
    ];

    colors : [3]Vector4 = .[
        .{1.0, 0.0, 0.0, 1.0},
        .{0.0, 1.0, 0.0, 1.0},
        .{0.0, 0.0, 1.0, 1.0},
    ];

    posvbo : GLuint;
    glCreateBuffers(1, *posvbo);
    glNamedBufferData(posvbo, size_of(type_of(positions)), positions.data, GL_DYNAMIC_DRAW);

    colvbo : GLuint;
    glCreateBuffers(1, *colvbo);
    glNamedBufferData(colvbo, size_of(type_of(colors)), colors.data, GL_DYNAMIC_DRAW);

    vao : GLuint;
    glCreateVertexArrays(1, *vao);

    glVertexArrayVertexBuffer(vao, 0, posvbo, 0, size_of(Vector2));
    glVertexArrayVertexBuffer(vao, 1, colvbo, 0, size_of(Vector4));

    glEnableVertexArrayAttrib(vao, 0);
    glEnableVertexArrayAttrib(vao, 1);

    glVertexArrayAttribFormat(vao, 0, 2, GL_FLOAT, GL_FALSE, 0);
    glVertexArrayAttribFormat(vao, 1, 4, GL_FLOAT, GL_FALSE, 0);

    glVertexArrayAttribBinding(vao, 0, 0);
    glVertexArrayAttribBinding(vao, 1, 1);

    proj : Matrix4 = orthographic_projection_matrix(0.0, xx windowwidth, 0.0, xx windowheight, -1.0, 1.0);

    ImGui.IMGUI_CHECKVERSION();
    ImGui.CreateContext();
    io : *ImGui.IO = ImGui.GetIO();
    io.ConfigFlags_ |= ImGui.ConfigFlags.NavEnableKeyboard;     // Enable Keyboard Controls
    io.ConfigFlags_ |= ImGui.ConfigFlags.NavEnableGamepad;      // Enable Gamepad Controls

    // Setup Dear ImGui style
    ImGui.StyleColorsDark();
    //ImGui::StyleColorsLight();

    // Setup Platform/Renderer backends
    ImGui_ImplSDL3_InitForOpenGL(window, glcontext);
    glsl_version := "#version 150";
    ImGui_ImplOpenGL3_Init(*glsl_version);

    while running {
        event : SDL_Event;
        while SDL_PollEvent(*event) {

            ImGui_ImplSDL3_ProcessEvent(*event);

            if event.type == {
                case xx SDL_EVENT_QUIT;
                    running = false;
                case xx SDL_EVENT_KEY_UP;
                    if event.key.key == SDLK_ESCAPE
                        running = false;
                case xx SDL_EVENT_WINDOW_RESIZED;
                    windowwidth = event.window.data1;
                    windowheight = event.window.data2;

                case xx SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED;
                    windowwidth = event.window.data1;
                    windowheight = event.window.data2;
            }
        }

        glViewport(0, 0, xx windowwidth, xx windowheight);
        glClear(GL_COLOR_BUFFER_BIT);

        pos += .{0.001, 0.001};

        model := identity_of(Matrix4);
        model = translate(model, make_vector3(pos, 0.0));

        glBindVertexArray(vao);
        glUseProgram(program);

        glUniformMatrix4fv(glGetUniformLocation(program, "proj"), 1, GL_TRUE, cast(*float) *proj);
        glUniformMatrix4fv(glGetUniformLocation(program, "model"), 1, GL_TRUE, cast(*float) *model);

        glDrawArrays(GL_TRIANGLES, 0, 3);

        // Start the Dear ImGui frame
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplSDL3_NewFrame();
        ImGui.NewFrame();

        ImGui.ShowDemoWindow();

        // Rendering
        ImGui.Render();
        ImGui_ImplOpenGL3_RenderDrawData(ImGui.GetDrawData());

        SDL_GL_SwapWindow(window);
    }

   // Cleanup
    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplSDL3_Shutdown();
    ImGui.DestroyContext();

    SDL_DestroyWindow(window);
    SDL_Quit();
}
