#include "../Engine/Engine.h"
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include <vector>
#include <cstdio>
#include "../Engine/Entity/Entity.h"
#include "../Engine/Components/TransformComponent.h"
#include "../Engine/Components/ModelComponent.h"
#include "../Engine/Components/LightComponent.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "stb_image.h"

using std::vector;

// Global light icon texture.
GLuint g_LightIconTex = 0;
ImTextureID g_LightIconImTex = 0;

// Helper function to load a texture.
bool LoadIconTexture(const char* filepath, GLuint &texOut, ImTextureID &imTexOut) {
    int w, h, channels;
    unsigned char* data = stbi_load(filepath, &w, &h, &channels, 0);
    if (!data) {
        printf("Failed to load icon texture: %s\n", filepath);
        return false;
    }
    // Overwrite loaded data with white pixels (all channels set to 255)
    int total = w * h * channels;
    for (int i = 0; i < total; i++) {
        data[i] = 255;
    }
    glGenTextures(1, &texOut);
    glBindTexture(GL_TEXTURE_2D, texOut);
    GLenum format = (channels == 3) ? GL_RGB : GL_RGBA;
    glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, format, GL_UNSIGNED_BYTE, data);
    glGenerateMipmap(GL_TEXTURE_2D);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    stbi_image_free(data);
    imTexOut = (ImTextureID)(intptr_t)texOut;
    return true;
}


// Editor camera.
struct EditorCamera {
    glm::vec3 position = glm::vec3(0.0f, 0.0f, 8.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;
    // Load the light icon.
    LoadIconTexture("assets/icons/light.png", g_LightIconTex, g_LightIconImTex);

    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);

    vector<Entity*> entities;
    // Create two cube entities.
    Entity* cube1 = new Entity(EntityType::CUBE);
    cube1->transform.position = glm::vec3(-2.0f, 0.0f, 0.0f);
    cube1->modelComponent = new ModelComponent();
    cube1->modelComponent->diffuseColor = glm::vec3(0.8f, 0.2f, 0.2f);
    cube1->modelComponent->specularColor = glm::vec3(1.0f, 1.0f, 1.0f);
    cube1->modelComponent->shininess = 32.0f;

    Entity* cube2 = new Entity(EntityType::CUBE);
    cube2->transform.position = glm::vec3(2.0f, 0.0f, 0.0f);
    cube2->modelComponent = new ModelComponent();
    cube2->modelComponent->diffuseColor = glm::vec3(0.2f, 0.8f, 0.2f);
    cube2->modelComponent->specularColor = glm::vec3(1.0f, 1.0f, 1.0f);
    cube2->modelComponent->shininess = 16.0f;

    // Create two light entities.
    Entity* light1 = new Entity(EntityType::LIGHT);
    light1->transform.position = glm::vec3(0.0f, 10.0f, 0.0f);
    light1->lightComponent = new LightComponent();
    light1->lightComponent->color = glm::vec3(1.0f, 1.0f, 1.0f);
    light1->lightComponent->intensity = 1.5f;

    Entity* light2 = new Entity(EntityType::LIGHT);
    light2->transform.position = glm::vec3(0.0f, -10.0f, 0.0f);
    light2->lightComponent = new LightComponent();
    light2->lightComponent->color = glm::vec3(0.2f, 0.2f, 1.0f);
    light2->lightComponent->intensity = 1.0f;

    entities.push_back(cube1);
    entities.push_back(cube2);
    entities.push_back(light1);
    entities.push_back(light2);

    Entity* selectedEntity = nullptr;
    int selectedIndex = -1;

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

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

        int winWidth, winHeight;
        glfwGetFramebufferSize(Engine::GetWindow(), &winWidth, &winHeight);
        editorCamera.projection = glm::perspective(glm::radians(45.0f), (float)winWidth / winHeight, 0.1f, 100.0f);

        Engine::ResizeFramebuffer(winWidth, winHeight);
        ImTextureID offscreenTexture = Engine::RenderScene(editorCamera.view, editorCamera.projection, editorCamera.position, entities);

        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glViewport(0, 0, winWidth, winHeight);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);



        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();

        ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode);

        // Left Panel: Entity List.
        ImGui::Begin("Entity List");
        for (int i = 0; i < entities.size(); i++) {
            char label[32];
            sprintf(label, "Entity %d", i);
            if (ImGui::Selectable(label, selectedIndex == i))
                selectedIndex = i;
            if (ImGui::IsItemClicked()) {
                selectedEntity = entities[i];
                selectedIndex = i;
            }
        }
        ImGui::End();

        // Right Panel: Entity Inspector.
        ImGui::Begin("Entity Inspector");
        if (selectedEntity) {
            ImGui::Text("Transform");
            ImGui::DragFloat3("Position", glm::value_ptr(selectedEntity->transform.position), 0.1f);
            ImGui::DragFloat3("Rotation", glm::value_ptr(selectedEntity->transform.rotation), 0.5f);
            ImGui::DragFloat3("Scale", glm::value_ptr(selectedEntity->transform.scale), 0.1f);
            if (selectedEntity->GetType() == EntityType::CUBE && selectedEntity->modelComponent) {
                ImGui::Separator();
                ImGui::Text("Model Properties");
                ImGui::ColorEdit3("Diffuse", glm::value_ptr(selectedEntity->modelComponent->diffuseColor));
                ImGui::ColorEdit3("Specular", glm::value_ptr(selectedEntity->modelComponent->specularColor));
                ImGui::DragFloat("Shininess", &selectedEntity->modelComponent->shininess, 1.0f, 1.0f, 128.0f);
            }
            if (selectedEntity->GetType() == EntityType::LIGHT && selectedEntity->lightComponent) {
                ImGui::Separator();
                ImGui::Text("Light Properties");
                ImGui::ColorEdit3("Light Color", glm::value_ptr(selectedEntity->lightComponent->color));
                ImGui::DragFloat("Intensity", &selectedEntity->lightComponent->intensity, 0.1f, 0.0f, 10.0f);
            }
        } else {
            ImGui::Text("No entity selected.");
        }
        ImGui::End();

        // Bottom Panel: Rendered Output.
        ImGui::Begin("Rendered Output");
        ImVec2 viewportSize = ImGui::GetContentRegionAvail();
        ImGui::Image(offscreenTexture, viewportSize, ImVec2(0,1), ImVec2(1,0));
        ImGui::End();

        ImGui::Render();
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

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

    for (auto e : entities)
        delete e;

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