#include <iostream>
#include <cmath>

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

#include <vector>
#include <ctime>

// GLM
#include <glm/glm.hpp>
#include <glm/ext.hpp>
#include <glm/gtc/matrix_transform.hpp>

#include "window/Window.h"
#include "window/Camera.h"
#include "window/Events.h"
#include "graphics/Mesh.h"
#include "graphics/Shader.h"
#include "render/render.h"


#define SPEED_FACTOR 2.5f


static void updateCameraPosition(Camera& cam, float delta) {
    static float camX = 0, camY= 0;

    if (Events::_cursor_locked){
        camY += -Events::deltaY / (float)Window::height * 2;
        camX += -Events::deltaX / (float)Window::height * 2;

        if (camY < -glm::radians(89.0f)){
            camY = -glm::radians(89.0f);
        }
        if (camY > glm::radians(89.0f)){
            camY = glm::radians(89.0f);
        }

        cam.rotation = glm::mat4(1.0f);
        cam.rotate(camY, camX, 0);
    }

    cam.updateVectors();

    glm::vec3 dir(0,0,0);
    if (Events::pressed(GLFW_KEY_W)){
        dir.x += cam.dir.x;
        dir.z += cam.dir.z;
    }
    if (Events::pressed(GLFW_KEY_S)){
        dir.x -= cam.dir.x;
        dir.z -= cam.dir.z;
    }
    if (Events::pressed(GLFW_KEY_D)){
        dir.x += cam.right.x;
        dir.z += cam.right.z;
    }
    if (Events::pressed(GLFW_KEY_A)){
        dir.x -= cam.right.x;
        dir.z -= cam.right.z;
    }

    if (Events::pressed(GLFW_KEY_SPACE)) {
        dir.y = 1;
    }

    if (Events::pressed(GLFW_KEY_LEFT_SHIFT)) {
        dir.y = -1;
    }

    if (dir.x != 0 || dir.y != 0 || dir.z != 0) {
        dir = glm::normalize(dir);
    }

    cam.position += dir * delta * SPEED_FACTOR;
}


void mainloop(Camera& camera) {
    long frame = 0;
    float lastTime = Window::getTime();
    float delta;

    bool devdata = false;
    Window::swapInterval(0);
    while (!Window::isShouldClose()){
        frame++;
        float currentTime = Window::getTime();
        delta = currentTime - lastTime;
        lastTime = currentTime;

        if (Events::jpressed(GLFW_KEY_ESCAPE)){
            Window::setShouldClose(true);
        }
        if (Events::jpressed(GLFW_KEY_TAB)){
            Events::toggleCursor();
        }
        if (Events::jpressed(GLFW_KEY_F)){
            devdata = !devdata;
            if (devdata) {
                glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
            } else {
                glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
            }
        }

        updateCameraPosition(camera, delta);

        renderScene(camera);

        Window::swapBuffers();
        Events::pullEvents();
    }
}


int main() {
    Window::initialize(800, 600, "Lab2");
    Events::initialize();

    if (loadResources() != 0) {
        std::cerr << "Failed to load resources" << std::endl;
        return 1;
    }

//    glDisable(GL_DEPTH_TEST);
//    glDisable(GL_CULL_FACE);
    glEnable(GL_DEPTH_TEST);

    Camera cam(glm::vec3(0, 0, 5), glm::radians(90.0f));

    mainloop(cam);

    unloadResources();

    Events::finalize();
    Window::terminate();
    return 0;
}
