#include <iostream>
#include <cmath>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <GL/glu.h>
#include "imgui.h"
#include "imgui_internal.h"  // For Im_PI
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include "VoxelGame.h"

// Global camera variables.
float yaw = -90.0f;
float pitch = 0.0f;
float lastX = 400.0f, lastY = 300.0f;
bool firstMouse = true;
float cameraPos[3] = {32.0f, 32.0f, 80.0f};
float cameraFront[3] = {0.0f, 0.0f, -1.0f};
float cameraUp[3] = {0.0f, 1.0f, 0.0f};

void mouse_callback(GLFWwindow* window, double xpos, double ypos) {
    if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_NORMAL)
        return;
    if (firstMouse) {
        lastX = static_cast<float>(xpos);
        lastY = static_cast<float>(ypos);
        firstMouse = false;
    }
    float xoffset = static_cast<float>(xpos) - lastX;
    float yoffset = lastY - static_cast<float>(ypos);
    lastX = static_cast<float>(xpos);
    lastY = static_cast<float>(ypos);
    
    float sensitivity = 0.1f;
    xoffset *= sensitivity;
    yoffset *= sensitivity;
    
    yaw += xoffset;
    pitch += yoffset;
    if (pitch > 89.0f) pitch = 89.0f;
    if (pitch < -89.0f) pitch = -89.0f;
    
    float radYaw = yaw * static_cast<float>(IM_PI) / 180.0f;
    float radPitch = pitch * static_cast<float>(IM_PI) / 180.0f;
    
    cameraFront[0] = cos(radYaw) * cos(radPitch);
    cameraFront[1] = sin(radPitch);
    cameraFront[2] = sin(radYaw) * cos(radPitch);
    float len = std::sqrt(cameraFront[0]*cameraFront[0] +
                          cameraFront[1]*cameraFront[1] +
                          cameraFront[2]*cameraFront[2]);
    cameraFront[0] /= len;
    cameraFront[1] /= len;
    cameraFront[2] /= len;
}

GLFWwindow* initWindow(int width, int height, const char* title) {
    if (!glfwInit()) {
        std::cerr << "Failed to initialize GLFW\n";
        return nullptr;
    }
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);
    GLFWwindow* window = glfwCreateWindow(width, height, title, nullptr, nullptr);
    if (!window) {
        std::cerr << "Failed to create GLFW window\n";
        glfwTerminate();
        return nullptr;
    }
    glfwMakeContextCurrent(window);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
    
    glewExperimental = GL_TRUE;
    if (glewInit() != GLEW_OK) {
        std::cerr << "GLEW Error\n";
        return nullptr;
    }
    return window;
}

int main() {
    GLFWwindow* window = initWindow(800, 600, "Voxel Game");
    if (!window)
        return -1;
    
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO &io = ImGui::GetIO(); (void)io;
    ImGui::StyleColorsDark();
    ImGui_ImplGlfw_InitForOpenGL(window, true);
    ImGui_ImplOpenGL3_Init("#version 330");
    
    VoxelGame game;
    if (!game.init()) {
        std::cerr << "Failed to initialize game\n";
        return -1;
    }
    
    float deltaTime = 0.0f, lastFrame = 0.0f;
    static bool cursorEnabled = false;
    static bool f1Pressed = false;
    
    while (!glfwWindowShouldClose(window)) {
        float currentFrame = static_cast<float>(glfwGetTime());
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;
        glfwPollEvents();
        
        // Toggle cursor lock with F1 (if ImGui isn’t capturing the mouse).
        if (glfwGetKey(window, GLFW_KEY_F1) == GLFW_PRESS && !ImGui::GetIO().WantCaptureMouse) {
            if (!f1Pressed) {
                cursorEnabled = !cursorEnabled;
                glfwSetInputMode(window, GLFW_CURSOR, cursorEnabled ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_DISABLED);
                f1Pressed = true;
                firstMouse = true;
            }
        } else if (glfwGetKey(window, GLFW_KEY_F1) == GLFW_RELEASE) {
            f1Pressed = false;
        }
        
        float cameraSpeed = 5.0f * deltaTime;
        if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
            cameraPos[0] += cameraFront[0] * cameraSpeed;
            cameraPos[1] += cameraFront[1] * cameraSpeed;
            cameraPos[2] += cameraFront[2] * cameraSpeed;
        }
        if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {
            cameraPos[0] -= cameraFront[0] * cameraSpeed;
            cameraPos[1] -= cameraFront[1] * cameraSpeed;
            cameraPos[2] -= cameraFront[2] * cameraSpeed;
        }
        if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {
            float right[3] = {
                cameraFront[1]*cameraUp[2] - cameraFront[2]*cameraUp[1],
                cameraFront[2]*cameraUp[0] - cameraFront[0]*cameraUp[2],
                cameraFront[0]*cameraUp[1] - cameraFront[1]*cameraUp[0]
            };
            float rLen = std::sqrt(right[0]*right[0] + right[1]*right[1] + right[2]*right[2]);
            right[0] /= rLen; right[1] /= rLen; right[2] /= rLen;
            cameraPos[0] -= right[0] * cameraSpeed;
            cameraPos[1] -= right[1] * cameraSpeed;
            cameraPos[2] -= right[2] * cameraSpeed;
        }
        if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {
            float right[3] = {
                cameraFront[1]*cameraUp[2] - cameraFront[2]*cameraUp[1],
                cameraFront[2]*cameraUp[0] - cameraFront[0]*cameraUp[2],
                cameraFront[0]*cameraUp[1] - cameraFront[1]*cameraUp[0]
            };
            float rLen = std::sqrt(right[0]*right[0] + right[1]*right[1] + right[2]*right[2]);
            right[0] /= rLen; right[1] /= rLen; right[2] /= rLen;
            cameraPos[0] += right[0] * cameraSpeed;
            cameraPos[1] += right[1] * cameraSpeed;
            cameraPos[2] += right[2] * cameraSpeed;
        }
        
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();
        
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(45.0, 800.0/600.0, 0.1, 200.0);
        
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        float centerX = cameraPos[0] + cameraFront[0];
        float centerY = cameraPos[1] + cameraFront[1];
        float centerZ = cameraPos[2] + cameraFront[2];
        gluLookAt(cameraPos[0], cameraPos[1], cameraPos[2],
                  centerX, centerY, centerZ,
                  cameraUp[0], cameraUp[1], cameraUp[2]);
        
        game.update(deltaTime, cameraPos);
        game.render();
        game.debugUI();
        
        ImGui::Render();
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
        glfwSwapBuffers(window);
    }
    
    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();
    glfwDestroyWindow(window);
    glfwTerminate();
    return 0;
}
