#include "../Engine/Engine.h"
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

// Simple editor camera.
struct EditorCamera {
    glm::vec3 position = glm::vec3(0.0f, 0.0f, 5.0f);
    float yaw = -90.0f;
    float pitch = 0.0f;
    float speed = 5.0f;
    float sensitivity = 0.1f;
    glm::mat4 view = glm::mat4(1.0f);
    glm::mat4 projection = glm::mat4(1.0f);
};

EditorCamera editorCamera;

void ProcessEditorCamera(GLFWwindow* window, float deltaTime) {
    glm::vec3 front;
    front.x = cos(glm::radians(editorCamera.yaw)) * cos(glm::radians(editorCamera.pitch));
    front.y = sin(glm::radians(editorCamera.pitch));
    front.z = sin(glm::radians(editorCamera.yaw)) * cos(glm::radians(editorCamera.pitch));
    front = glm::normalize(front);
    glm::vec3 right = glm::normalize(glm::cross(front, glm::vec3(0,1,0)));

    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
        editorCamera.position += front * editorCamera.speed * deltaTime;
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
        editorCamera.position -= front * editorCamera.speed * deltaTime;
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
        editorCamera.position -= right * editorCamera.speed * deltaTime;
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
        editorCamera.position += right * editorCamera.speed * deltaTime;

    static double lastX = 0, lastY = 0;
    if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS) {
        double xpos, ypos;
        glfwGetCursorPos(window, &xpos, &ypos);
        if (lastX == 0 && lastY == 0) { lastX = xpos; lastY = ypos; }
        float offsetX = (float)(xpos - lastX);
        float offsetY = (float)(lastY - ypos);
        lastX = xpos;
        lastY = ypos;
        editorCamera.yaw   += offsetX * editorCamera.sensitivity;
        editorCamera.pitch += offsetY * editorCamera.sensitivity;
        if (editorCamera.pitch > 89.0f)  editorCamera.pitch = 89.0f;
        if (editorCamera.pitch < -89.0f) editorCamera.pitch = -89.0f;
    } else {
        lastX = lastY = 0;
    }
    editorCamera.view = glm::lookAt(editorCamera.position, editorCamera.position + front, glm::vec3(0,1,0));
}

int main() {
    if (!Engine::Init())
        return 1;

    // Setup ImGui.
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO();
    io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
    ImGui::StyleColorsDark();

    const char* glsl_version = "#version 330";
    ImGui_ImplGlfw_InitForOpenGL(Engine::GetWindow(), true);
    ImGui_ImplOpenGL3_Init(glsl_version);

    float lastFrameTime = (float)glfwGetTime();

    while (!glfwWindowShouldClose(Engine::GetWindow())) {
        float currentFrameTime = (float)glfwGetTime();
        float deltaTime = currentFrameTime - lastFrameTime;
        lastFrameTime = currentFrameTime;

        ProcessEditorCamera(Engine::GetWindow(), deltaTime);

        // Update editor camera projection based on the window size.
        int winWidth, winHeight;
        glfwGetFramebufferSize(Engine::GetWindow(), &winWidth, &winHeight);
        editorCamera.projection = glm::perspective(glm::radians(45.0f), (float)winWidth / winHeight, 0.1f, 100.0f);

        // Offscreen rendering: Resize framebuffer and render scene.
        Engine::ResizeFramebuffer(winWidth, winHeight);
        ImTextureID offscreenTexture = Engine::RenderScene(editorCamera.view, editorCamera.projection, editorCamera.position);

        // Clear the default framebuffer background.
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glViewport(0, 0, winWidth, winHeight);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Clear with black (or choose any color).
        glClear(GL_COLOR_BUFFER_BIT);

        // Start a single ImGui frame.
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();

        // Create a full-viewport dock space.
        ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode);


        // Create an "Editor Panel" window.
        ImGui::Begin("Editor Panel");
        ImGui::Text("Welcome to the Editor!");
        // (Additional UI elements can go here.)
        ImGui::End();

        // Create a "Rendered Output" window.
        ImGui::Begin("Rendered Output");
        // Get available region size for the rendered output.
        ImVec2 viewportSize = ImGui::GetContentRegionAvail();
        // Display the offscreen texture. The UVs are flipped to correct the upside-down image.
        ImGui::Image(offscreenTexture, viewportSize, ImVec2(0,1), ImVec2(1,0));
        ImGui::End();

        // Finalize and render the ImGui frame.
        ImGui::Render();
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

        glfwSwapBuffers(Engine::GetWindow());
        glfwPollEvents();
    }

    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();
    Engine::Shutdown();
    return 0;
}
