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

#include <math.h>

#include <GL/glew.h>
#include <GLFW/glfw3.h>

#include <cglm/struct.h>

#include "worldgen.h"
#include "shaders.h"
#include "initialization.h"
#include "renderer.h"

#define FOV 1.4f

#define SHADER_BUF_SIZE 1000

GLFWwindow** window;

static GLfloat vertexBufferData[1000];

void updateWindow(GLFWwindow** window, GLuint vertexBuffer);
void terminateProgram(GLuint* vertexBufferPtr, GLuint* vertexArrayIDPtr, GLuint programID);
bool shouldTerminate(GLFWwindow** window);

int main()
{
    // start initialization
    initializeWindowSystem(&window);

    GLuint vertexArrayID;
    glGenVertexArrays(1, &vertexArrayID);
    glBindVertexArray(vertexArrayID);

    glEnable(GL_DEPTH_TEST);
    GLuint programID = loadShaders( "src/shader.vert", "src/shader.frag" );

    GLuint matrixID = glGetUniformLocation(programID, "MVP");

    mat4s projection = glms_perspective(FOV, 16.0f / 9.0f, 0.1f, 100.0f);
    shaderSetMat4(programID, "projection", &projection);

    vec3s eye = {0.0f, 0.0f, 5.0f};
    vec3s center = {0.0f, 0.0f, 0.0f};
    vec3s up = {0.0f, 1.0f, 0.0f};
    
    GLuint vertexBuffer;
    initializeVertexBuffer(&vertexBuffer, &vertexBufferData);
    
    vec3s direction = {0.0f, 0.0f, -5.0f};

    do
    {
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

        mat4s view = glms_lookat(eye, center, up);
        mat4s model = glms_mat4_identity();

        // start shaders
        glUseProgram(programID);
        
        shaderSet3f(programID, "objectColor", 1.0f, 0.5f, 0.31f);
        shaderSet3f(programID, "lightColor", 1.0f, 1.0f, 1.0f);
        shaderSet3f(programID, "lightPos", 1.0f, 1.0f, 1.0f);
        shaderSetVec3(programID, "viewPos", &eye);

        shaderSetMat4(programID, "view", &view);
        shaderSetMat4(programID, "model", &model);
        
        updateWindow(window, vertexBuffer);
    }
    while(!shouldTerminate(window));

    terminateProgram(&vertexBuffer, &vertexArrayID, programID);
    return 0;
}

void updateWindow(GLFWwindow** window, GLuint vertexBuffer)
{
        glEnableVertexAttribArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);

        glVertexAttribPointer(
            0,
            3,
            GL_FLOAT,
            GL_FALSE,
            0,
            (void*)0
        );

        glDrawArrays(GL_TRIANGLES, 0, 20*3);
        glDisableVertexAttribArray(0);

        glfwSwapBuffers(window);
        glfwPollEvents();
}

void terminateProgram(GLuint* vertexBufferPtr, GLuint* vertexArrayIDPtr, GLuint programID)
{
    glDeleteBuffers(1, vertexBufferPtr);
    glDeleteProgram(programID);
    glDeleteVertexArrays(1, vertexArrayIDPtr);

    glfwTerminate();
}

bool shouldTerminate(GLFWwindow** window)
{
    // quit on <ESC>, <Q>, or when otherwise told to
    return glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS
        || glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS
        || glfwWindowShouldClose(window);
}
