#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#include "libs/cglm/cglm.h"
#include "libs/glad/include/glad/glad.h"
#include <SDL2/SDL.h>
// #include <SDL2/SDL_events.h>
#include "stb_image.h"
// #include <SDL2/SDL_image.h>
// #include <SDL2/SDL_render.h>
#include "lib.h"

//Screen dimension constants
const int SCREEN_WIDTH = 1024;
const int SCREEN_HEIGHT = 768;

int main(void) {
    SDL_Window *window = NULL;

    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        printf("error initializing SDL: %s\n", SDL_GetError());
        return 1;
    }

    SDL_GL_LoadLibrary(NULL);

    // Request an OpenGL 4.5 context (should be core)
    SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5);
    // Also request a depth buffer
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);

    window = SDL_CreateWindow("Terrain",
                              SDL_WINDOWPOS_UNDEFINED,
                              SDL_WINDOWPOS_UNDEFINED,
                              SCREEN_WIDTH,
                              SCREEN_HEIGHT,
                              SDL_WINDOW_OPENGL );

    if (window == NULL) {
        printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
        return 1;
    }

    SDL_GL_CreateContext(window);
    gladLoadGLLoader(SDL_GL_GetProcAddress);

    printf("Vendor:   %s\n", glGetString(GL_VENDOR));
    printf("Renderer: %s\n", glGetString(GL_RENDERER));
    printf("Version:  %s\n", glGetString(GL_VERSION));

    SDL_GL_SetSwapInterval(1);

    int w, h, c;
    unsigned char *terrain_img = stbi_load("terrain.png", &w, &h, &c, 0);
    int vcount = w * h * 3;
    float *verts = malloc(vcount * sizeof(float));
    float scale_y = 1.0f;
    float shift_y = 16.0f;
    for (int row = 0; row < h; row++) {
        for (int col = 0; col < w; col++) {
            unsigned char *texel = terrain_img + (row * w + col) * c;
            unsigned char y = texel[0];

            float *v = &verts[(row * w + col) * 3];
            *(v+0) = -w/2 + col;
            *(v+1) = y * scale_y + shift_y;
            *(v+2) = -h/2 + row;
        }
    }
    printf("Loaded %d vertices\n", w * h);
    stbi_image_free(terrain_img);

    unsigned int icount = w * (h - 1) * 2;
    unsigned int *indices = malloc(icount * sizeof(unsigned int));
    for (int row = 0; row < h - 1; row++) {
        for (int col = 0; col < w; col++) {
            unsigned int idx1 = w * (row + 0) + col;
            unsigned int idx2 = w * (row + 1) + col;
            *(indices + idx1) = idx1;
            *(indices + idx2) = idx2;
        }
    }

    const unsigned int NUM_STRIPS = h - 1;
    const unsigned int NUM_VERTS_PER_STRIP = w * 2;

    unsigned int terrainVAO, terrainVBO, terrainIBO;
    glGenVertexArrays(1, &terrainVAO);
    glBindVertexArray(terrainVAO);

    glGenBuffers(1, &terrainVBO);
    glBindBuffer(GL_ARRAY_BUFFER, terrainVBO);
    glBufferData(GL_ARRAY_BUFFER, vcount * sizeof(float), verts, GL_STATIC_DRAW);

    // position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    glGenBuffers(1, &terrainIBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, terrainIBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, icount * sizeof(unsigned), indices, GL_STATIC_DRAW);
    // Disable depth test and face culling.
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);
    int screen_width,screen_height;
    SDL_GetWindowSize(window, &screen_width, &screen_height);
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);

    GLuint shaderProgram =
        compileShaderProgram("shaders/main.vert", "shaders/main.frag", NULL);

    int matrixLocation;
    mat4 projection;
    glm_mat4_identity(projection);

    float aspectRatio = (float)SCREEN_WIDTH / (float)SCREEN_HEIGHT;
    glm_perspective(45.0f, aspectRatio, 0.1f, 100.0f, projection);
    glUseProgram(shaderProgram);
    SDL_Event e;
    bool quit = false;
    Uint64 now = SDL_GetPerformanceCounter();
    Uint64 last = 0;
    double dt = 0;
    float x = 0.0f, y = 0.0f;
    while (quit == false) {
        last = now;
        now = SDL_GetPerformanceCounter();
        dt = (double)((now - last)*1000 / (double)SDL_GetPerformanceFrequency());
        while (SDL_PollEvent( &e)) {
            if (e.type == SDL_QUIT) {
                quit = true;
            } else {
                char key = e.key.keysym.sym;
                float speed = 1.1f;
                if (key == 'w') {
                    y += speed;
                }
                if (key == 's') {
                    y -= speed;
                }
                if (key == 'a') {
                    x += speed;
                }
                if (key == 'd') {
                    x -= speed;
                }
            }
        }

        mat4 model;
        glm_mat4_identity(model);
        mat4 view;
        glm_mat4_identity(view);
        glm_translate(model, (vec3){x, y, 0.0f});
        // glm_translate(model, (vec3){10.0f, -10.0f, 0.0f});
        // glm_rotate(model, now * 0.0000000001f, (vec3){0.0f, 0.0f, 1.0f});


        matrixLocation = glGetUniformLocation(shaderProgram, "view");
        glUniformMatrix4fv(matrixLocation, 1, GL_FALSE, view[0]);
        matrixLocation = glGetUniformLocation(shaderProgram, "projection");
        glUniformMatrix4fv(matrixLocation, 1, GL_FALSE, projection[0]);
        matrixLocation = glGetUniformLocation(shaderProgram, "model");
        glUniformMatrix4fv(matrixLocation, 1, GL_FALSE, model[0]);

        glViewport(0, 0, screen_width, screen_height);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        for (unsigned strip = 0; strip < NUM_STRIPS; strip++) {
            glDrawElements(GL_TRIANGLE_STRIP,   // primitive type
                           NUM_VERTS_PER_STRIP,   // number of indices to render
                           GL_UNSIGNED_INT,     // index data type
                           (void*)(sizeof(unsigned) * (NUM_VERTS_PER_STRIP+2) * strip)); // offset to starting index
        }

        SDL_GL_SwapWindow(window);
    }

    SDL_DestroyWindow( window );
    return 0;
}
