#include <iostream>
#include <cmath>

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

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

#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"

#include "VoxelGame.h"

// camera globals
static glm::vec3 cameraPos   = {32.0f, 32.0f, 80.0f};
static glm::vec3 cameraFront = {0.0f, 0.0f, -1.0f};
static glm::vec3 cameraUp    = {0.0f, 1.0f,  0.0f};
static float yaw   = -90.0f, pitch = 0.0f;
static bool  firstMouse = true;
static float lastX, lastY;

static void mouse_callback(GLFWwindow* window, double xpos, double ypos) {
    if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_NORMAL)
        return;  // do not rotate if cursor is free

    if (firstMouse) {
        lastX = float(xpos);
        lastY = float(ypos);
        firstMouse = false;
    }
    float dx = float(xpos) - lastX;
    float dy = lastY - float(ypos);
    lastX = float(xpos);
    lastY = float(ypos);

    const float sensitivity = 0.1f;
    dx *= sensitivity;
    dy *= sensitivity;

    yaw   += dx;
    pitch += dy;
    pitch = std::clamp(pitch, -89.0f, 89.0f);

    float ry = glm::radians(yaw);
    float rp = glm::radians(pitch);
    cameraFront = glm::normalize(glm::vec3(
        cos(ry)*cos(rp),
        sin(rp),
        sin(ry)*cos(rp)
    ));
}

static GLFWwindow* initWindow(int w, int h, const char* title) {
    if (!glfwInit()) return nullptr;
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
#endif
    GLFWwindow* win = glfwCreateWindow(w, h, title, nullptr, nullptr);
    if (!win) { glfwTerminate(); return nullptr; }
    glfwMakeContextCurrent(win);
    glewExperimental = GL_TRUE;
    if (glewInit() != GLEW_OK) {
        glfwDestroyWindow(win);
        glfwTerminate();
        return nullptr;
    }
    glViewport(0, 0, w, h);
    glfwSetCursorPosCallback(win, mouse_callback);
    // start with cursor disabled
    glfwSetInputMode(win, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
    lastX = w / 2.0f;
    lastY = h / 2.0f;
    return win;
}

int main() {
    const int SCR_W = 1280, SCR_H = 720;
    GLFWwindow* window = initWindow(SCR_W, SCR_H, "Voxel CSM");
    if (!window) return -1;

    // ImGui
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGui_ImplGlfw_InitForOpenGL(window, true);
    ImGui_ImplOpenGL3_Init("#version 330");
    ImGui::StyleColorsDark();

    // Our game
    VoxelGame game;
    if (!game.init(SCR_W, SCR_H)) {
        std::cerr << "Failed to init VoxelGame\n";
        return -1;
    }

    float lastFrame = 0.0f;
    bool cursorEnabled = false;
    bool f1Pressed = false;

    while (!glfwWindowShouldClose(window)) {
        float now = float(glfwGetTime());
        float dt  = now - lastFrame;
        lastFrame = now;

        glfwPollEvents();

        // Toggle cursor with F1 (when ImGui not capturing)
        ImGuiIO& io = ImGui::GetIO();
        if (glfwGetKey(window, GLFW_KEY_F1) == GLFW_PRESS && !io.WantCaptureMouse) {
            if (!f1Pressed) {
                cursorEnabled = !cursorEnabled;
                glfwSetInputMode(window, GLFW_CURSOR,
                    cursorEnabled ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_DISABLED);
                f1Pressed = true;
                firstMouse = true;  // reset for mouse callback
            }
        } else if (glfwGetKey(window, GLFW_KEY_F1) == GLFW_RELEASE) {
            f1Pressed = false;
        }

        // WASD movement
        float speed = 5.0f * dt;
        if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
            cameraPos += cameraFront * speed;
        if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
            cameraPos -= cameraFront * speed;
        glm::vec3 right = glm::normalize(glm::cross(cameraFront, cameraUp));
        if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
            cameraPos -= right * speed;
        if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
            cameraPos += right * speed;

        // Build view/proj
        glm::mat4 view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
        glm::mat4 proj = glm::perspective(glm::radians(45.0f),
                                          float(SCR_W)/SCR_H, 0.1f, 200.0f);

        // Update & render
        game.update(dt, cameraPos);
        game.render(view, proj);

        // Debug UI
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();
        game.debugUI();
        ImGui::Render();
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

        glfwSwapBuffers(window);
    }

    // Cleanup
    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();
    glfwDestroyWindow(window);
    glfwTerminate();
    return 0;
}