#include "Engine.h"
#include "Entitys/Object.h"
#include "Renderer.h"
#include "components/SpriteComponent.h"
#include "components/CameraComponent.h"
#include "components/LightComponent.h"
#include "components/TilemapComponent.h"
#include "components/ScriptComponent.h"
#include "components/PhysicsComponent.h"
#include "components/ParticleComponent.h"
#include "components/AnimationComponent.h"

#include "core/utils/FileDialog.h"
#include "core/utils/Logging.h"

#include "core/audio/AudioEngine.h"

#include "core/utils/EngineConfig.h"
#include "utils/GameObjectsList.h"
#include "core/utils/Profiler.h"
#include "core/utils/utils.h"
#include "core/utils/LoadingWindow.h"

#include "editor/windows/AssetBrowser.h"
#include "editor/windows/Inspector.h"
#include "editor/windows/AudioInfo.h"

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <imgui.h>
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h>

#include <functional>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <vector>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <random>
#include <format>
#include <memory>
#include <filesystem>
#include <iostream>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <commdlg.h>
#include <openssl/sha.h>
#include <fstream>
#include <json.hpp>
using json = nlohmann::json;

static std::shared_ptr<Object> selected = nullptr;
static bool playing = false;
static bool lastPlaying = false;

static glm::vec2 cameraPos = {0, 0};
static float cameraZoom = 1.0f;
static ImVec2 lastMousePos = {};

static glm::vec2 editorCameraSavedPos = {0.0f, 0.0f};
static float editorCameraSavedZoom = 1.0f;
static bool editorCameraWasSaved = false;

static std::shared_ptr<CameraComponent> m_activeCamera;

float g_fps = 0.0f;

static const std::string tempScenePath = "__tmp_scene.yaml";

GLFWwindow *window = nullptr;

Engine::Engine()
{

    Init();
}

Engine::~Engine()
{
    Shutdown();
}

void DrawProfilerTimelineBars(const ProfileNode &node,
                              int depth,
                              float baseOffsetX,
                              float frameDuration,
                              ImVec2 origin)
{
    constexpr float rowHeight = 24.0f;
    constexpr float textPadding = 5.0f;

    constexpr size_t SEED = 9873;

    // compute bar rect
    float startRel = node.visualStartMs / frameDuration;
    float durRel = node.visualDurationMs / frameDuration;
    float x = origin.x + startRel * baseOffsetX;
    float width = std::fmax(1.0f, durRel * baseOffsetX);
    float y = origin.y + depth * rowHeight;

    ImVec2 barMin(x, y);
    ImVec2 barMax(x + width, y + rowHeight - 4.0f);

    // draw the bar
    size_t hash = std::hash<std::string>{}(node.name);
    ImU32 color = ImColor::HSV(((hash ^ SEED) % 1000) / 1000.0f, 0.6f, 0.85f);

    ImDrawList *draw = ImGui::GetWindowDrawList();
    draw->AddRectFilled(barMin, barMax, color, 3.0f);
    draw->AddRect(barMin, barMax, IM_COL32_BLACK, 3.0f);

    // how much room we have for text
    float availW = width - textPadding * 2;
    const char *textToDraw = nullptr;

    // 1) try the short "XX.XX ms"
    char timeBuf[16];
    int tn = std::snprintf(timeBuf, sizeof(timeBuf), "%.2f ms", node.visualDurationMs);
    if (tn > 0)
    {
        ImVec2 sz = ImGui::CalcTextSize(timeBuf);
        if (sz.x <= availW)
        {
            textToDraw = timeBuf;
        }
    }

    // 2) only if the short fits, see if full fits
    if (textToDraw == timeBuf)
    {
        char fullBuf[64];
        int fn = std::snprintf(fullBuf, sizeof(fullBuf),
                               "%s (%.2f ms)",
                               node.name.c_str(),
                               node.visualDurationMs);
        if (fn > 0)
        {
            ImVec2 fsz = ImGui::CalcTextSize(fullBuf);
            if (fsz.x <= availW)
            {
                textToDraw = fullBuf;
            }
        }
    }

    // 3) draw if we decided on something
    if (textToDraw)
    {
        draw->AddText(
            ImVec2(barMin.x + textPadding,
                   barMin.y),
            IM_COL32_WHITE,
            textToDraw);
    }

    // tooltip
    ImVec2 mouse = ImGui::GetMousePos();
    if (mouse.x >= barMin.x && mouse.x <= barMax.x &&
        mouse.y >= barMin.y && mouse.y <= barMax.y)
    {
        ImGui::SetTooltip(
            "%s\nCurrent: %.3f ms\nSmoothed: %.3f ms",
            node.name.c_str(),
            node.durationMs,
            node.visualDurationMs);
    }

    // recurse
    for (auto &child : node.children)
        DrawProfilerTimelineBars(child,
                                 depth + 1,
                                 baseOffsetX,
                                 frameDuration,
                                 origin);
}

inline int CountNodes(const ProfileNode &root) noexcept
{
    int count = 0;
    std::vector<const ProfileNode *> stack;
    stack.reserve(32);
    stack.push_back(&root);

    while (!stack.empty())
    {
        const ProfileNode *node = stack.back();
        stack.pop_back();
        ++count;
        for (const auto &child : node->children)
            stack.push_back(&child);
    }
    return count;
}

inline ProfileNode GetAveragedFrameCopy(const ProfileNode &src)
{
    ProfileNode avg(src.name, src.startMs);
    avg.durationMs = src.durationMs;
    avg.visualStartMs = avg.startMs;
    avg.visualDurationMs = avg.durationMs;

    const auto &kids = src.children;
    avg.children.reserve(kids.size());
    for (const auto &c : kids)
        avg.children.push_back(GetAveragedFrameCopy(c));

    return avg;
}

void ShowProfilerTimeline()
{
    PROFILE_ENGINE_SCOPE("Engine::ShowProfilerTimeline");

    if (!g_engineConfig.settings.show_profiler_window)
    {
        return;
    }

    if (!g_engineConfig.settings.profile_enabled)
    {
        if (ImGui::Begin("Profiler", nullptr, ImGuiWindowFlags_NoScrollbar))
        {
            ImGui::Text("Profiling Disabled.");
            ImGui::End();
        }
        return;
    }

    // Statics & timing
    static ProfileNode cachedFrame;
    static double lastUpdate = 0.0;
    static float updateInterval = 0.25f;
    static bool freezeView = false;
    static float zoom = 1.0f;

    double now = ImGui::GetTime();
    auto latest = profiler.GetLatestFrame();

    if (!freezeView && latest && now - lastUpdate >= updateInterval)
    {
        cachedFrame = GetAveragedFrameCopy(*latest);
        lastUpdate = now;
    }

    if (cachedFrame.durationMs <= 0.0)
    {
        ImGui::Begin("Profiler", nullptr, ImGuiWindowFlags_NoScrollbar);
        ImGui::Text("Waiting for profiler data...");
        ImGui::End();
        return;
    }

    ImGui::Begin("Profiler", nullptr, ImGuiWindowFlags_NoScrollbar);

    ImGui::Checkbox("Freeze View", &freezeView);
    ImGui::SameLine();
    ImGui::SetNextItemWidth(120.0f);
    ImGui::DragFloat("Update Interval", &updateInterval, 0.05f, 0.05f, 5.0f, "%.2fs");

    updateInterval = std::clamp(updateInterval, 0.05f, 5.0f);

    ImGui::SameLine();
    ImGui::DragFloat("Zoom", &zoom, 0.1f, 0.1f, 10.0f, "×%.1f");
    zoom = std::max(0.1f, zoom);

    ImGui::SameLine();
    if (ImGui::Button("Export"))
    {
        json root = SerializeProfileNode(cachedFrame);
        std::ofstream out("profile_export.json");
        if (out)
        {
            out << root.dump(4);
            Logger::LogInfo("Profiler exported to profile_export.json");
        }
        else
        {
            Logger::LogError("Failed to write profiler export.");
        }
    }

    // Layout metrics
    constexpr float rowH = 24.0f;
    int rows = CountNodes(cachedFrame);
    float avail = ImGui::GetContentRegionAvail().x;
    float width = avail * zoom;
    float height = std::min(rows * rowH, 400.0f);

    // Begin child & get origin
    ImGui::BeginChild("TimelineScroll", ImVec2(0, height), false, ImGuiWindowFlags_HorizontalScrollbar);
    ImVec2 origin = ImGui::GetCursorScreenPos();
    ImDrawList *draw = ImGui::GetWindowDrawList();
    ImU32 gridCol = ImGui::GetColorU32(ImGuiCol_Border);

    struct Line
    {
        ImVec2 a, b;
    };

    static float lastW = -1.0f;
    static int lastR = -1;
    static std::vector<Line> gridLines;
    if (lastW != width || lastR != rows)
    {
        gridLines.clear();
        // horizontal
        for (int r = 0; r <= rows; ++r)
        {
            float y = r * rowH;
            gridLines.push_back({{0, y}, {width, y}});
        }
        // vertical
        float dx = width / 10.0f;
        for (int i = 0; i <= 10; ++i)
        {
            float x = i * dx;
            gridLines.push_back({{x, 0}, {x, rows * rowH}});
        }
        lastW = width;
        lastR = rows;
    }

    // Draw cached grid
    for (auto &ln : gridLines)
    {
        draw->AddLine(
            {origin.x + ln.a.x, origin.y + ln.a.y},
            {origin.x + ln.b.x, origin.y + ln.b.y},
            gridCol);
    }

    // Bars
    DrawProfilerTimelineBars(cachedFrame, 0, width, cachedFrame.visualDurationMs, origin);

    // Ensure scrollable size
    ImGui::Dummy(ImVec2(width, rows * rowH));
    ImGui::EndChild();

    ImGui::Text("Frame: %.2f ms | Rate: %.2fs",
                cachedFrame.visualDurationMs,
                updateInterval);

    ImGui::End();
}

void Engine::ShowDebugOverlay(float deltaTime)
{
    if (!g_engineConfig.settings.show_performance_window)
    {
        return;
    }

    // === Performance Window ===
    static std::vector<float> fpsHistory;
    static const int maxFpsHistory = 100;
    static float fpsTimer = 0.0f;
    float fps = 1.0f / deltaTime;
    g_fps = fps;
    if (fpsHistory.size() >= maxFpsHistory)
        fpsHistory.erase(fpsHistory.begin());
    if (fpsTimer >= 0.05f)
    {
        fpsTimer = 0;
        fpsHistory.push_back(fps);
    }
    fpsTimer += deltaTime;

    ImGui::Begin("Performance Info", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize);

    ImGui::TextColored(ImVec4(0.3f, 1.0f, 0.3f, 1.0f), "Performance");
    ImGui::Separator();
    ImGui::Text("FPS:        %.1f", fps);
    ImGui::Text("Delta Time: %.4f s", deltaTime);
    ImGui::Text("Frame Time: %.2f ms", deltaTime * 1000.0f);

    if (!fpsHistory.empty())
    {
        float maxFps = *std::max_element(fpsHistory.begin(), fpsHistory.end());
        maxFps = std::max(maxFps * 1.1f, 60.0f);

        ImGui::PlotLines("##FPS", fpsHistory.data(), fpsHistory.size(), 0, "FPS History",
                         0.0f, maxFps, ImVec2(-1, 50));
    }
    else
    {
        ImGui::Text("FPS data unavailable");
    }

    ImGui::Spacing();
    ImGui::TextColored(ImVec4(0.4f, 0.7f, 1.0f, 1.0f), "Renderer");
    ImGui::Separator();
    ImGui::Text("Draw Calls:     %d", Renderer::GetDrawCallCount());
    ImGui::Text("Lua OnUpdate Calls:   %d", m_OnUpdateCalls);
    ImGui::Text("Scene Lights:   %d / %d", Renderer::GetLightsCount(), g_engineConfig.gl_maxLight);
    ImGui::Text("Reserved Draws: %d", m_Reserved_draws);
    ImGui::Text("Camera Zoom:    %.2f", cameraZoom);
    ImGui::Text("Camera Pos:     (%.1f, %.1f)", cameraPos.x, cameraPos.y);

    ImGui::End();
}

void ShowColorCorrectionWindow()
{
    PROFILE_ENGINE_SCOPE("Engine::ShowColorCorrectionWindow");

    ColorCorrection *cc = Renderer::GetColorCorrection();
    if (!cc)
        return;

    ImGui::Begin("Color Correction");

    bool changed = false;
    changed |= ImGui::SliderFloat("Brightness", &cc->brightness, 0.0f, 2.0f, "%.2f");
    changed |= ImGui::SliderFloat("Saturation", &cc->saturation, 0.0f, 2.0f, "%.2f");
    changed |= ImGui::SliderFloat("Gamma", &cc->gamma, 0.1f, 4.0f, "%.2f");

    ImGui::SeparatorText("Bloom");

    changed |= ImGui::Checkbox("Enable Bloom", &cc->bloom);
    changed |= ImGui::SliderFloat("Threshold", &cc->threshold, 0.0f, 2.0f, "%.2f");
    changed |= ImGui::SliderFloat("Intensity", &cc->intensity, 0.0f, 4.0f, "%.2f");

    if (ImGui::Button("Reset to Default"))
    {
        *cc = ColorCorrection();
    }
    ImGui::SameLine();

    ImGui::End();
}

void Engine::Init()
{

    if (DeleteLatestLogFile())
    {
        Logger::LogVerbose("Log file deleted");
    }
    else
    {
        Logger::LogVerbose("Failed to delete log file");
    }

    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    window = glfwCreateWindow(1280, 720, "Create Editor", nullptr, nullptr);
    glfwMakeContextCurrent(window);
    glfwSwapInterval(0); // No VSync
    glewInit();

    g_engineConfig.LoadFromFile();
    Logger::LogOk("Config Core");

    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO &io = ImGui::GetIO();
    io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
    ImGui::StyleColorsDark();
    ImGui_ImplGlfw_InitForOpenGL(window, true);
    ImGui_ImplOpenGL3_Init((std::string("#version ") + g_engineConfig.gl_version).c_str());
    m_Reserved_draws = 0;

    Renderer::Init();

    auto obj = std::make_shared<Object>("Hello, Create");

    objects.push_back(obj);

    selected = obj;

    //{
    //    std::random_device rd;
    //    std::mt19937 rng(rd());
    //    std::uniform_real_distribution<float> lightRadius(200.0f, 1000.0f);
    //    std::uniform_real_distribution<float> lightIntensity(0.5f, 1.0f);
    //    std::uniform_real_distribution<float> hueDist(0.0f, 1.0f);
    //
    //    const float tileSize = 2048.0f; // approximate texture size
    //    const int gridSize = 10;
    //
    //    // 10x10 grid of pebble sprites
    //    for (int y = 0; y < gridSize; ++y)
    //    {
    //        for (int x = 0; x < gridSize; ++x)
    //        {
    //            int index = y * gridSize + x;
    //            auto obj = std::make_shared<Object>("Pebble_" + std::to_string(index));
    //            obj->SetLocalPosition(glm::vec2(x * tileSize, y * tileSize));
    //
    //            auto sprite = obj->AddComponent<SpriteComponent>();
    //            sprite->SetTexture("C:/Users/spenc/OneDrive/Pictures/textures/ganges_river_pebbles_diff_1k.png");
    //            sprite->SetNormalMap("C:/Users/spenc/OneDrive/Pictures/textures/ganges_river_pebbles_nor_gl_1k.png");
    //            sprite->SetRenderType(RenderType::Lit);
    //
    //            objects.push_back(obj);
    //        }
    //    }
    //
    //    // 100 random lights
    //    std::uniform_real_distribution<float> posDist(0.0f, tileSize * gridSize);
    //
    //    for (int i = 0; i < 100; ++i)
    //    {
    //        auto obj = std::make_shared<Object>("Light_" + std::to_string(i));
    //        obj->SetLocalPosition(glm::vec2(posDist(rng), posDist(rng)));
    //
    //        auto light = obj->AddComponent<LightComponent>();
    //        light->SetRadius(lightRadius(rng));
    //        light->SetIntensity(lightIntensity(rng));
    //        light->SetColor(HSVtoRGB(hueDist(rng), 0.8f, 1.0f));
    //
    //        objects.push_back(obj);
    //    }
    //}

    Logger::LogVerbose("Init Sound Core");

    AudioEngine::Init();

    // AssetManager::LoadAssetAsync("C:\\Users\\spenc\\Music\\creative-technology-showreel-241274.mp3", AssetType::Audio);

    Logger::LogVerbose("Resverving Objects");

    // These values were AI Generated.

    m_toDraw.reserve(2048);           // ~2K sprites per frame (including UI, particles, objects)
    m_scriptUpdates.reserve(256);     // ~256 active script components per frame
    m_collectStack.reserve(2048);     // Used for internal temp memory, 512 is safe unless deep recursion
    m_physicsUpdates.reserve(512);    // 256-512 physics bodies/entities with Box2D
    m_particleUpdates.reserve(24);    // ~1K particle systems active is fair in effects-heavy scenes
    m_animationsUpdates.reserve(512); // ~500 animated objects (characters, FX, etc.)

    Logger::LogOk("Engine Core");
}

core::types::Vec2 ScreenToWorld(const core::types::Vec2 &screenPos, const core::types::Vec2 &viewportSize, const core::types::Vec2 &cameraPos, float zoom)
{
    core::types::Vec2 viewportCenter = viewportSize * 0.5f;
    core::types::Vec2 world = (screenPos - viewportCenter) / zoom + cameraPos;
    return world;
}

void Engine::collectObjects(bool playing, const glm::vec2 &camPos, float camZoom)
{

    m_activeCamera = nullptr;

    m_toDraw.clear();
    m_scriptUpdates.clear();
    m_animationsUpdates.clear();
    m_collectStack.clear();
    m_physicsUpdates.clear();
    m_particleUpdates.clear();

    const glm::vec2 screenSize = glm::vec2(Renderer::GetSize());

    for (auto &root : objects)
        if (!root->GetParent())
            m_collectStack.push_back(root);

    while (!m_collectStack.empty())
    {
        auto obj = m_collectStack.back();
        m_collectStack.pop_back();

        if (!obj->GetVisable())
            continue;

        m_toDraw.push_back(obj.get());

        if (auto light = obj->GetComponent<LightComponent>())
        {
            glm::vec2 world = obj->GetWorldPosition();
            glm::vec2 screen = (world - camPos) * camZoom + screenSize * 0.5f;
            Renderer::AddLight(screen, light->GetColor(), light->GetIntensity(), light->GetRadius() * camZoom);
        }

        if (!m_activeCamera)
        {
            if (auto camera = obj->GetComponent<CameraComponent>())
            {
                if (camera->IsPrimary())
                    m_activeCamera = camera;
            }
        }

        if (auto particles = obj->GetComponent<ParticleComponent>())
            m_particleUpdates.push_back(particles.get());
        if (auto animator = obj->GetComponent<AnimationComponent>())
            m_animationsUpdates.push_back(animator.get());

        if (playing)
        {
            if (auto script = obj->GetComponent<ScriptComponent>())
                m_scriptUpdates.push_back(script.get());

            if (auto physics = obj->GetComponent<PhysicsComponent>())
                m_physicsUpdates.push_back(physics.get());
        }

        for (auto &child : obj->GetChildren())
            m_collectStack.push_back(child);
    }
}

void Engine::Run()
{
    Logger::LogOk("Engine Start");

    while (!glfwWindowShouldClose(window))
    {
        if (g_engineConfig.settings.profile_editor)
        {
            profiler.BeginFrame();
        }

        profiler.BeginEngineSection("glfwPollEvents");
        glfwPollEvents();
        profiler.EndEngineSection();

        profiler.BeginEngineSection("NewFrame");

        profiler.BeginEngineSection("ImGui_ImplOpenGL3_NewFrame");
        ImGui_ImplOpenGL3_NewFrame();
        profiler.EndEngineSection();

        profiler.BeginEngineSection("ImGui_ImplGlfw_NewFrame");
        ImGui_ImplGlfw_NewFrame();
        profiler.EndEngineSection();

        profiler.BeginEngineSection("ImGui::NewFrame");
        ImGui::NewFrame();
        profiler.EndEngineSection();

        profiler.BeginEngineSection("ImGui::DockSpaceOverViewport");
        ImGui::DockSpaceOverViewport(ImGui::GetMainViewport()->ID);
        profiler.EndEngineSection();

        profiler.BeginEngineSection("glfwGetTime");
        float currentTime = glfwGetTime();
        static float lastTime = currentTime;
        float deltaTime = currentTime - lastTime;
        lastTime = currentTime;
        profiler.EndEngineSection();

        profiler.EndEngineSection(); // End NewFrame

        profiler.BeginEngineSection("Engine::ShowDebugOverlay");
        ShowDebugOverlay(deltaTime);
        profiler.EndEngineSection();

        profiler.BeginEngineSection("Logger::Draw");
        Logger::Draw();
        profiler.EndEngineSection();

        profiler.BeginEngineSection("BeginMainMenuBar");

        if (ImGui::BeginMainMenuBar())
        {
            if (ImGui::Button(playing ? "Stop" : "Play"))
            {
                if (!playing)
                {
                    Logger::LogVerbose("[RestoreScene] Saving original scene");
                    SaveState();
                }
                else
                {
                    Logger::LogVerbose("[RestoreScene] Reloading original scene");
                    LoadState();
                }
                selected = nullptr;
                playing = !playing;
            }

            if (ImGui::BeginMenu("File"))
            {
                if (ImGui::MenuItem("Save Scene"))
                {
                    std::string file = CreateFileDialog(FileDialogType::Scenes);
                    if (!file.empty())
                        SaveScene(file);
                    selected = nullptr;
                }
                if (ImGui::MenuItem("Load Scene"))
                {
                    std::string file = OpenFileDialog(FileDialogType::Scenes);
                    Logger::LogInfo("[LoadScene] Loading Scene.");
                    if (!file.empty())
                        LoadScene(file);
                    selected = nullptr;
                }
                ImGui::EndMenu();
            }

            if (ImGui::BeginMenu("Options"))
            {
                if (ImGui::BeginMenu("Settings"))
                {
                    ImGui::Checkbox("Enable Lighting", &g_engineConfig.settings.lighting_enabled);
                    ImGui::Checkbox("Enable Gizmos", &g_engineConfig.settings.draw_gizmos);
                    if (ImGui::BeginMenu("Profiling"))
                    {
                        ImGui::Text("Note: Slow");
                        ImGui::Checkbox("Profiling", &g_engineConfig.settings.profile_enabled);
                        if (g_engineConfig.settings.profile_enabled)
                        {
                            ImGui::Checkbox("Profile Engine", &g_engineConfig.settings.profile_editor);
                            ImGui::Checkbox("Deep Profiling", &g_engineConfig.settings.profile_deep);
                            // ImGui::Checkbox("GPU Profiling", &g_engineConfig.settings.profile_gpu);
                        }
                        ImGui::EndMenu();
                    }
                    ImGui::EndMenu();
                }

                if (ImGui::BeginMenu("Windows"))
                {
                    ImGui::Checkbox("Color Correction", &g_engineConfig.settings.show_color_correction_window);
                    ImGui::Checkbox("Profiler", &g_engineConfig.settings.show_profiler_window);
                    ImGui::Checkbox("Assets", &g_engineConfig.settings.show_asset_window);
                    ImGui::Checkbox("Performance Info", &g_engineConfig.settings.show_performance_window);
                    ImGui::Checkbox("Audio Viewer", &g_engineConfig.settings.show_audio_window);

                    ImGui::EndMenu();
                }

                ImGui::EndMenu();
            }

            const char *version_fmt = "Create Engine v%s";
            char buf[64];
            std::snprintf(buf, sizeof(buf), version_fmt, g_engineConfig.version.c_str());

            float textWidth = ImGui::CalcTextSize(buf).x;
            float avail = ImGui::GetWindowSize().x - ImGui::GetCursorPosX();
            ImGuiStyle &style = ImGui::GetStyle();
            ImGui::SameLine(avail - textWidth - style.FramePadding.x);
            ImGui::TextUnformatted(buf);

            ImGui::EndMainMenuBar();
        }

        profiler.EndEngineSection();

        if (g_engineConfig.settings.show_color_correction_window)
            ShowColorCorrectionWindow();

        ShowAssetBrowser();
        DrawAudioPlayingList();

        {
            PROFILE_ENGINE_SCOPE("Engine::DrawSceneTree");

            ImGui::Begin("Scene Tree");

            if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) &&
                ImGui::IsKeyPressed(ImGuiKey_Delete) && selected)
            {
                ImGui::OpenPopup("Confirm Deletion");
            }

            if (ImGui::BeginPopupModal("Confirm Deletion", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
            {
                ImGui::Text("Are you sure you want to delete this object?");
                ImGui::Separator();

                if (ImGui::Button("Delete"))
                {
                    pendingDeletion.push_back(selected);
                    selected = nullptr;
                    ImGui::CloseCurrentPopup();
                }

                ImGui::SameLine();

                if (ImGui::Button("Cancel"))
                {
                    ImGui::CloseCurrentPopup();
                }

                ImGui::EndPopup();
            }

            if (ImGui::BeginPopupContextWindow("SceneTreeContext", ImGuiPopupFlags_MouseButtonRight))
            {
                if (ImGui::MenuItem("Create New"))
                {
                    auto obj = std::make_shared<Object>("NewObject");
                    objects.push_back(obj);
                    selected = obj;
                    ImGui::OpenPopup("RenameObject");
                }
                ImGui::Separator();
                if (ImGui::MenuItem("Create New Sprite"))
                {
                    auto obj = std::make_shared<Object>("NewSprite");
                    obj->AddComponent<SpriteComponent>();
                    objects.push_back(obj);
                    selected = obj;
                    ImGui::OpenPopup("RenameObject");
                }
                if (ImGui::MenuItem("Create New Light"))
                {
                    auto obj = std::make_shared<Object>("NewSprite");
                    obj->AddComponent<LightComponent>();
                    objects.push_back(obj);
                    selected = obj;
                    ImGui::OpenPopup("RenameObject");
                }
                ImGui::EndPopup();
            }

            for (auto &obj : objects)
                if (!obj->GetParent())
                    DrawObjectNode(obj);

            ImGui::End();
        }

        DrawInspectorUI(selected);

        ImGui::Begin("Viewport", nullptr, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
        ImVec2 size = ImGui::GetContentRegionAvail();

        static bool lastPlaying = false;
        if (playing && !lastPlaying)
        {
            editorCameraSavedPos = cameraPos;
            editorCameraSavedZoom = cameraZoom;
            editorCameraWasSaved = true;
        }
        else if (!playing && lastPlaying && editorCameraWasSaved)
        {
            cameraPos = editorCameraSavedPos;
            cameraZoom = editorCameraSavedZoom;
            editorCameraWasSaved = false;
        }
        lastPlaying = playing;

        if (playing && m_activeCamera)
        {
            cameraPos = m_activeCamera->GetOwner()->GetWorldPosition();
            cameraZoom = m_activeCamera->GetZoom();
        }

        if (!playing && ImGui::IsWindowHovered())
        {
            if (ImGui::IsMouseDragging(ImGuiMouseButton_Right))
            {
                ImVec2 delta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right);
                ImGui::ResetMouseDragDelta(ImGuiMouseButton_Right);
                cameraPos -= glm::vec2(delta.x, delta.y) / cameraZoom;
            }

            float wheel = ImGui::GetIO().MouseWheel;
            if (wheel != 0.0f)
            {
                float zoomFactor = (wheel > 0) ? 1.1f : 1.0f / 1.1f;

                ImVec2 mousePos = ImGui::GetMousePos();
                ImVec2 windowPos = ImGui::GetWindowPos();
                ImVec2 contentMin = ImGui::GetWindowContentRegionMin();

                glm::vec2 relativeMouse = glm::vec2(
                    mousePos.x - windowPos.x - contentMin.x,
                    mousePos.y - windowPos.y - contentMin.y);

                glm::vec2 before = (relativeMouse - glm::vec2(size.x / 2, size.y / 2)) / cameraZoom + cameraPos;

                cameraZoom *= zoomFactor;
                cameraZoom = std::clamp(cameraZoom, 0.1f, 10.0f);

                glm::vec2 after = (relativeMouse - glm::vec2(size.x / 2, size.y / 2)) / cameraZoom + cameraPos;

                cameraPos += before - after;
            }
        }

        profiler.BeginEngineSection("Renderer::Resize");
        Renderer::Resize((int)size.x, (int)size.y);
        profiler.EndEngineSection();

        profiler.BeginEngineSection("Renderer::Begin");
        Renderer::Begin();
        profiler.EndEngineSection();

        profiler.BeginEngineSection("Reserve");
        m_toDraw.reserve(m_Reserved_draws);
        m_Reserved_draws = 0;
        m_toDraw.clear();
        m_scriptUpdates.clear();
        m_particleUpdates.clear();
        profiler.EndEngineSection();

        profiler.BeginEngineSection("Collect Objects");
        collectObjects(playing, cameraPos, cameraZoom);
        profiler.EndEngineSection();

        profiler.BeginEngineSection("Renderer::UpdateClusterLights");
        Renderer::UpdateClusterLights();
        profiler.EndEngineSection();

        profiler.BeginEngineSection("Sort Objects");
        if (m_toDraw.size() > 1)
        {
            std::sort(m_toDraw.begin(), m_toDraw.end(),
                      [](auto const &a, auto const &b)
                      {
                          if (a->layer != b->layer)
                              return a->layer < b->layer;
                          return a->GetWorldPosition().y < b->GetWorldPosition().y;
                      });
        }
        profiler.EndEngineSection();

        m_OnUpdateCalls = 0;

        if (!g_engineConfig.settings.profile_editor)
        {
            profiler.BeginFrame();
        }
        profiler.BeginSection("Updates");

        profiler.BeginSection("Scripts");
        for (auto *script : m_scriptUpdates)
        {
            profiler.BeginSection("Object: " + script->GetOwner()->GetName());
            script->OnUpdate(deltaTime);
            m_OnUpdateCalls++;
            profiler.EndSection();
        }
        profiler.EndSection();

        profiler.BeginSection("Particles");
        for (auto *script : m_particleUpdates)
        {
            profiler.BeginSection("Object: " + script->GetOwner()->GetName());
            script->Update(deltaTime);
            profiler.EndSection();
        }
        profiler.EndSection();

        profiler.BeginSection("Animations");
        for (auto *animation : m_animationsUpdates)
        {
            profiler.BeginSection("Object: " + animation->GetOwner()->GetName());
            animation->Update(deltaTime);
            profiler.EndSection();
        }
        profiler.EndSection();

        profiler.EndSection();

        AudioEngine::Update();

        profiler.BeginSection("Render");
        for (auto *obj : m_toDraw)
        {
            const core::types::Vec2 worldPos = obj->GetWorldPosition();

            if (auto sprite = obj->GetComponent<SpriteComponent>())
            {
                profiler.BeginSection("Draw Sprite: " + obj->GetName());
                Renderer::DrawSprite(sprite.get(), worldPos, cameraZoom, cameraPos);
                profiler.EndSection();
            }

            if (auto tilemap = obj->GetComponent<TilemapComponent>())
            {
                profiler.BeginSection("Draw Tilemap: " + obj->GetName());
                Renderer::DrawTilemap(tilemap.get(), worldPos, cameraZoom, cameraPos);
                profiler.EndSection();
            }

            if (auto animator = obj->GetComponent<AnimationComponent>())
            {
                profiler.BeginSection("Draw Animator: " + obj->GetName());

                Renderer::DrawAnimator(animator.get(), worldPos, cameraZoom, cameraPos);

                profiler.EndSection();
            }

            if (auto particle = obj->GetComponent<ParticleComponent>())
            {
                profiler.BeginSection("Draw Particles: " + obj->GetName());

                const auto &particles = particle->GetParticles();
                const auto &settings = particle->GetSettings();

                for (const auto &p : particles)
                {
                    float t = std::clamp(1.0f - (p.life / settings.lifeMax), 0.0f, 1.0f);
                    core::types::Color color = core::types::Color::Lerp(settings.startColor, settings.endColor, t);

                    core::types::Vec2 pWorld = p.position;
                    core::types::Vec2 size = core::types::Vec2(p.size, p.size);
                    Renderer::BatchQuad(pWorld, size, p.rotation, color, cameraPos, cameraZoom, settings.roundness);
                }

                profiler.EndSection();
            }
        }
        profiler.EndSection();

        if (!g_engineConfig.settings.profile_editor)
        {
            profiler.EndFrame();
        }

        Renderer::End();

        profiler.BeginSection("Engine::DrawGameWindow");

        GLuint texID = Renderer::GetFinalTexture();
        ImGui::Image((ImTextureID)(uintptr_t)texID, size, ImVec2(0, 1), ImVec2(1, 0));

        ImVec2 winPos = ImGui::GetWindowPos();
        ImVec2 winSize = ImGui::GetWindowSize();
        ImVec2 mousePos = ImGui::GetMousePos();
        core::types::Vec2 viewportSize(winSize.x, winSize.y);
        core::types::Vec2 screenMousePos(mousePos.x - winPos.x, mousePos.y - winPos.y);

        bool mouseInWindow =
            screenMousePos.x >= 0 && screenMousePos.y >= 0 &&
            screenMousePos.x < viewportSize.x &&
            screenMousePos.y < viewportSize.y;

        if (selected && mouseInWindow)
        {
            auto tilemap = selected->GetComponent<TilemapComponent>();
            if (tilemap && tilemap->HasSelection())
            {
                const auto tileSize = tilemap->GetTileSize();
                const uint64_t uaid = tilemap->GetTextureUAID();
                const auto *asset = AssetManager::GetAssetByID(uaid);

                if (asset && asset->loaded)
                {
                    if (const auto *imageAsset = dynamic_cast<const ImageAssetInfo *>(asset))
                    {
                        auto *drawList = ImGui::GetWindowDrawList();
                        const auto texSize = imageAsset->size;
                        int maxCols = static_cast<int>(texSize.x / tileSize.x);
                        int maxRows = static_cast<int>(texSize.y / tileSize.y);

                        core::types::Vec2 screenMouse(mousePos.x - winPos.x, mousePos.y - winPos.y);
                        core::types::Vec2 worldMouse = ScreenToWorld(screenMouse, {winSize.x, winSize.y}, cameraPos, cameraZoom);

                        int centerCol = static_cast<int>(worldMouse.x / tileSize.x);
                        int centerRow = static_cast<int>(worldMouse.y / tileSize.y);

                        int radius = 15;
                        int minCol = centerCol - radius;
                        int maxCol = centerCol + radius;
                        int minRow = centerRow - radius;
                        int maxRow = centerRow + radius;

                        core::types::Vec2 screenCenter = {winPos.x + winSize.x * 0.5f, winPos.y + winSize.y * 0.5f};
                        float maxDist = static_cast<float>(radius) * tileSize.x * cameraZoom;

                        for (int y = minRow; y <= maxRow; ++y)
                        {
                            for (int x = minCol; x <= maxCol; ++x)
                            {
                                float gx = x * tileSize.x;
                                float gy = y * tileSize.y;

                                float sx = (gx - cameraPos.x) * cameraZoom + screenCenter.x;
                                float sy = (gy - cameraPos.y) * cameraZoom + screenCenter.y;
                                float sx2 = (gx + tileSize.x - cameraPos.x) * cameraZoom + screenCenter.x;
                                float sy2 = (gy + tileSize.y - cameraPos.y) * cameraZoom + screenCenter.y;

                                float centerGX = gx + tileSize.x * 0.5f;
                                float centerGY = gy + tileSize.y * 0.5f;
                                float dx = (centerGX - worldMouse.x);
                                float dy = (centerGY - worldMouse.y);
                                float dist = std::sqrt(dx * dx + dy * dy) * cameraZoom;
                                float fade = std::clamp(1.0f - dist / maxDist, 0.0f, 0.9f);
                                int alpha = static_cast<int>(fade * 255.0f);

                                ImU32 color = IM_COL32(255, 255, 0, alpha);

                                drawList->AddLine(ImVec2(sx, sy), ImVec2(sx2, sy), color);
                                drawList->AddLine(ImVec2(sx2, sy), ImVec2(sx2, sy2), color);
                                drawList->AddLine(ImVec2(sx2, sy2), ImVec2(sx, sy2), color);
                                drawList->AddLine(ImVec2(sx, sy2), ImVec2(sx, sy), color);
                            }
                        }

                        Texture *texture = tilemap->GetTexture().get();
                        int atlasCols = tilemap->GetAtlasColumns();
                        int atlasRows = tilemap->GetAtlasRows();

                        if (texture && texture->GetID() != 0 && atlasCols > 0 && atlasRows > 0)
                        {
                            int x0 = tilemap->selStartX;
                            int y0 = tilemap->selStartY;
                            int x1 = tilemap->selEndX;
                            int y1 = tilemap->selEndY;

                            // Normalize
                            if (x1 < x0)
                                std::swap(x0, x1);
                            if (y1 < y0)
                                std::swap(y0, y1);

                            int widthInTiles = x1 - x0 + 1;
                            int heightInTiles = y1 - y0 + 1;

                            float tileW = tileSize.x;
                            float tileH = tileSize.y;

                            float snappedGX = std::floor(worldMouse.x / tileW) * tileW;
                            float snappedGY = std::floor(worldMouse.y / tileH) * tileH;

                            float sx = (snappedGX - cameraPos.x) * cameraZoom + screenCenter.x;
                            float sy = (snappedGY - cameraPos.y) * cameraZoom + screenCenter.y;
                            float sx2 = (snappedGX + tileW * widthInTiles - cameraPos.x) * cameraZoom + screenCenter.x;
                            float sy2 = (snappedGY + tileH * heightInTiles - cameraPos.y) * cameraZoom + screenCenter.y;

                            float texWidth = static_cast<float>(texture->GetSize().x);
                            float texHeight = static_cast<float>(texture->GetSize().y);

                            float uvTileW = tileW / texWidth;
                            float uvTileH = tileH / texHeight;

                            ImVec2 uvMin(x0 * uvTileW, y0 * uvTileH);
                            ImVec2 uvMax((x1 + 1) * uvTileW, (y1 + 1) * uvTileH);

                            ImTextureID previewTexID = (ImTextureID)(uintptr_t)texture->GetID();
                            drawList->AddImage(previewTexID, ImVec2(sx, sy), ImVec2(sx2, sy2), uvMin, uvMax, IM_COL32(255, 255, 255, 180));

                            if (ImGui::IsMouseClicked(ImGuiMouseButton_Left))
                            {
                                int gx = static_cast<int>(snappedGX / tileSize.x);
                                int gy = static_cast<int>(snappedGY / tileSize.y);
                                tilemap->PlaceSelection(gx, gy);
                            }
                        }
                    }
                }
            }
        }

        if (ImGui::BeginDragDropTarget())
        {
            if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("ASSET_TEXTURE"))
            {
                if (payload->DataSize == sizeof(uint64_t))
                {
                    uint64_t uaid = *(const uint64_t *)payload->Data;

                    glm::vec2 worldPos = ScreenToWorld(screenMousePos, viewportSize, cameraPos, cameraZoom);

                    auto obj = std::make_shared<Object>("New Sprite");
                    obj->SetLocalPosition(worldPos);

                    auto sprite = obj->AddComponent<SpriteComponent>();
                    sprite->SetTexture(uaid);

                    objects.push_back(obj);
                }
            }
            ImGui::EndDragDropTarget();
        }

        ImGui::End();

        profiler.EndEngineSection();

        ShowProfilerTimeline();

        {
            PROFILE_ENGINE_SCOPE("Finish Rendering");

            profiler.BeginEngineSection("ImGui::Render");
            ImGui::Render();
            profiler.EndEngineSection();

            int w, h;
            profiler.BeginEngineSection("glfwGetFramebufferSize");
            glfwGetFramebufferSize(window, &w, &h);
            profiler.EndEngineSection();

            glViewport(0, 0, w, h);

            glClear(GL_COLOR_BUFFER_BIT);

            profiler.BeginEngineSection("ImGui_ImplOpenGL3_RenderDrawData");
            ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
            profiler.EndEngineSection();

            profiler.BeginEngineSection("glfwSwapBuffers");

            glfwSwapBuffers(window);

            profiler.EndEngineSection();
        }

        for (auto &obj : pendingDeletion)
        {
            Logger::LogVerbose("Deleting Object From Que: '%s', [%d, %s]", obj->GetName().c_str(), obj->uid.id, obj->uid.uuid.c_str());
            if (obj->GetParent())
                obj->GetParent()->RemoveChild(obj.get());
            else
                objects.erase(std::remove_if(objects.begin(), objects.end(),
                                             [&](const std::shared_ptr<Object> &o)
                                             { return o == obj; }),
                              objects.end());
        }
        pendingDeletion.clear();

        if (g_engineConfig.settings.profile_editor)
        {
            profiler.EndFrame();
        }
    }
}

void Engine::DrawObjectNode(const std::shared_ptr<Object> &obj)
{
    PROFILE_ENGINE_SCOPE("Engine::DrawObjectNode");

    ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow |
                               ImGuiTreeNodeFlags_SpanAvailWidth |
                               (obj == selected ? ImGuiTreeNodeFlags_Selected : 0);

    bool open = ImGui::TreeNodeEx((void *)(intptr_t)obj->uid.id, flags, "%s", obj->GetName().c_str());

    if (ImGui::IsItemClicked())
        selected = obj;

    // === Context Menu ===
    if (ImGui::BeginPopupContextItem())
    {
        if (ImGui::MenuItem("Delete"))
        {
            pendingDeletion.push_back(obj);
        }
        if (ImGui::MenuItem("Create Child"))
        {
            auto child = std::make_shared<Object>("NewObject");
            obj->AddChild(child);
            selected = child;
            ImGui::OpenPopup("RenameObject");
        }
        ImGui::EndPopup();
    }

    // === Drag Source ===
    if (ImGui::BeginDragDropSource())
    {
        ImGui::SetDragDropPayload("OBJECT", &obj, sizeof(obj));
        ImGui::Text("Move: %s", obj->GetName().c_str());
        ImGui::EndDragDropSource();
    }

    // === Drop Target ===
    if (ImGui::BeginDragDropTarget())
    {
        if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("OBJECT"))
        {
            auto dragged = *(std::shared_ptr<Object> *)payload->Data;

            if (dragged != obj && dragged)
            {
                Object *oldParent = dragged->GetParent();

                if (oldParent)
                {
                    oldParent->RemoveChild(dragged.get());

                    for (const auto &candidate : objects)
                    {
                        if (candidate.get() == oldParent)
                        {
                            pendingDeletion.push_back(candidate);
                            break;
                        }
                    }
                }
                else
                {
                    objects.erase(std::remove(objects.begin(), objects.end(), dragged), objects.end());
                }

                obj->AddChild(dragged);
            }
        }
        ImGui::EndDragDropTarget();
    }

    // === Children ===
    if (open)
    {
        for (auto &child : obj->GetChildren())
            DrawObjectNode(child);
        ImGui::TreePop();
    }
}

bool VerifySceneHash(const YAML::Node &root)
{
    if (!root["scene_hash"] || !root["objects"])
        return false;

    YAML::Emitter sceneOnly;
    sceneOnly << root["objects"];

    std::string sceneString = sceneOnly.c_str();
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256(reinterpret_cast<const unsigned char *>(sceneString.c_str()), sceneString.size(), hash);

    std::ostringstream hashHex;
    for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i)
        hashHex << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(hash[i]);

    return hashHex.str() == root["scene_hash"].as<std::string>();
}

void Engine::SaveScene(const std::string &path)
{
    YAML::Emitter out;
    YAML::Emitter sceneData;

    // Serialize object list only
    sceneData << YAML::BeginSeq;
    for (const auto &obj : objects)
        obj->Save(sceneData);
    sceneData << YAML::EndSeq;

    std::string objectData = sceneData.c_str();
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256(reinterpret_cast<const unsigned char *>(objectData.c_str()), objectData.size(), hash);

    std::ostringstream hashHex;
    for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i)
        hashHex << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(hash[i]);

    out << YAML::BeginMap;
    out << YAML::Key << "engine_version" << YAML::Value << g_engineConfig.version;
    out << YAML::Key << "scene_name" << YAML::Value << std::filesystem::path(path).stem().string();
    out << YAML::Key << "scene_hash" << YAML::Value << hashHex.str();
    out << YAML::Key << "format_version" << YAML::Value << 1;
    out << YAML::Key << "objects" << YAML::Value << YAML::Load(objectData);

    Logger::LogVerbose("[SaveScene] Saving Color Correction Attributes");

    out << YAML::Key << "color_correction" << YAML::BeginMap;
    out << YAML::Key << "brightness" << YAML::Value << Renderer::GetColorCorrection()->brightness;
    out << YAML::Key << "saturation" << YAML::Value << Renderer::GetColorCorrection()->saturation;
    out << YAML::Key << "gamma" << YAML::Value << Renderer::GetColorCorrection()->gamma;
    out << YAML::Key << "bloom" << YAML::Value << Renderer::GetColorCorrection()->bloom;
    out << YAML::Key << "intensity" << YAML::Value << Renderer::GetColorCorrection()->intensity;
    out << YAML::Key << "threshold" << YAML::Value << Renderer::GetColorCorrection()->threshold;
    out << YAML::EndMap;

    AssetManager::Save(out);

    out << YAML::EndMap;

    std::ofstream file(path);
    file << out.c_str();

    Logger::LogOk("Scene Saved");
}

void Engine::LoadScene(const std::string &path)
{
    Logger::LogDebug("[LoadScene] Reading Scene File.");
    YAML::Node root = YAML::LoadFile(path);

    LoadingWindow loadingUI;
    loadingUI.Create("Loading Scene");

    std::string currentStep = "Verifying Scene";
    std::string currentDetail = "";
    loadingUI.Update(currentStep, currentDetail, 0.0f);

    if (!root["engine_version"] || !root["format_version"] || !root["scene_name"])
    {
        Logger::LogError("[LoadScene] Missing required metadata!");
        loadingUI.Destroy();
        return;
    }

    if (root["engine_version"].as<std::string>() != "0.1.0")
    {
        Logger::LogError("[LoadScene] Version mismatch! Expected 0.1.0, got %s", root["engine_version"].as<std::string>().c_str());
        loadingUI.Destroy();
        return;
    }

    if (!VerifySceneHash(root))
    {
        Logger::LogWarning("[LoadScene] Scene hash does not match! File may be corrupted or tampered.");
    }

    if (root["Assets"])
    {
        currentStep = "Loading Assets";
        currentDetail = "Parsing asset data...";
        loadingUI.Update(currentStep, currentDetail, 0.05f);

        Logger::LogVerbose("[LoadScene] Loading Assets");
        AssetManager::Load(root["Assets"]);
    }

    Logger::LogDebug("[LoadScene] Reseting Scene.");
    currentStep = "Clearing Previous Scene";
    currentDetail = "Removing existing objects";
    loadingUI.Update(currentStep, currentDetail, 0.1f);

    objects.clear();

    Logger::LogDebug("[LoadScene] Recreating Objects");
    currentStep = "Creating Scene Objects";

    const auto &objectArray = root["objects"];
    int objectIndex = 0;
    int totalObjects = static_cast<int>(objectArray.size());

    for (const auto &node : objectArray)
    {
        std::string objName = node["name"] ? node["name"].as<std::string>() : ("Unnamed_" + std::to_string(objectIndex));
        currentDetail = "Loading: " + objName;
        float progress = 0.1f + (0.75f * (objectIndex / static_cast<float>(std::max(1, totalObjects))));
        loadingUI.Update(currentStep, currentDetail, progress);

        auto obj = std::make_shared<Object>(objName);
        obj->Load(node);
        objects.push_back(obj);
        objectIndex++;
    }

    if (root["color_correction"])
    {
        Logger::LogVerbose("[LoadScene] Loading Color Correction Attributes");
        currentStep = "Color Correction";
        currentDetail = "Applying settings...";
        loadingUI.Update(currentStep, currentDetail, 0.9f);

        ColorCorrection *cc = Renderer::GetColorCorrection();
        const auto &data = root["color_correction"];
        if (cc)
        {
            if (data["brightness"])
                cc->brightness = data["brightness"].as<float>();
            if (data["saturation"])
                cc->saturation = data["saturation"].as<float>();
            if (data["gamma"])
                cc->gamma = data["gamma"].as<float>();
            if (data["bloom"])
                cc->bloom = data["bloom"].as<bool>();
            if (data["intensity"])
                cc->intensity = data["intensity"].as<float>();
            if (data["threshold"])
                cc->threshold = data["threshold"].as<float>();
        }
    }

    currentStep = "Done";
    currentDetail = "Finalizing load...";
    loadingUI.Update(currentStep, currentDetail, 1.0f);

    Logger::LogOk("Scene Loaded: %s", root["scene_name"].as<std::string>().c_str());

    loadingUI.Destroy();
}

void Engine::SaveState()
{
    YAML::Emitter out;
    out << YAML::BeginMap;

    out << YAML::Key << "objects" << YAML::BeginSeq;
    for (const auto &obj : objects)
        obj->Save(out);
    out << YAML::EndSeq;

    Logger::LogVerbose("[SaveState] Saving Color Correction State");

    out << YAML::Key << "color_correction" << YAML::BeginMap;
    out << YAML::Key << "brightness" << YAML::Value << Renderer::GetColorCorrection()->brightness;
    out << YAML::Key << "saturation" << YAML::Value << Renderer::GetColorCorrection()->saturation;
    out << YAML::Key << "gamma" << YAML::Value << Renderer::GetColorCorrection()->gamma;
    out << YAML::Key << "bloom" << YAML::Value << Renderer::GetColorCorrection()->bloom;
    out << YAML::Key << "intensity" << YAML::Value << Renderer::GetColorCorrection()->intensity;
    out << YAML::Key << "threshold" << YAML::Value << Renderer::GetColorCorrection()->threshold;
    out << YAML::EndMap;

    AssetManager::Save(out);

    out << YAML::EndMap;

    savedStateYAML = out.c_str();
    Logger::LogVerbose("[SaveState] Scene serialized (%zu bytes)", savedStateYAML.size());
}

void Engine::LoadState()
{
    if (savedStateYAML.empty())
    {
        Logger::LogWarning("[LoadState] No scene state in memory.");
        return;
    }

    YAML::Node root;
    try
    {
        root = YAML::Load(savedStateYAML);
    }
    catch (const std::exception &e)
    {
        Logger::LogError("[LoadState] Failed to parse saved scene: %s", e.what());
        return;
    }

    if (root["Assets"])
    {
        Logger::LogVerbose("[LoadState] Loading Assets");
        AssetManager::Load(root["Assets"]);
    }

    objects.clear();

    const auto &objectArray = root["objects"];
    for (const auto &node : objectArray)
    {
        auto obj = std::make_shared<Object>("[DefaultObject]");
        obj->Load(node);
        objects.push_back(obj);
    }

    if (root["color_correction"])
    {
        Logger::LogVerbose("[LoadState] Loading Color Correction State");

        ColorCorrection *cc = Renderer::GetColorCorrection();
        const auto &data = root["color_correction"];
        if (cc)
        {
            if (data["brightness"])
                cc->brightness = data["brightness"].as<float>();
            if (data["saturation"])
                cc->saturation = data["saturation"].as<float>();
            if (data["gamma"])
                cc->gamma = data["gamma"].as<float>();
            if (data["bloom"])
                cc->bloom = data["bloom"].as<bool>();
            if (data["intensity"])
                cc->intensity = data["intensity"].as<float>();
            if (data["threshold"])
                cc->threshold = data["threshold"].as<float>();
        }
    }

    Logger::LogVerbose("[LoadState] Scene restored");
}

void Engine::Shutdown()
{
    g_engineConfig.SaveToFile();

    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();
    glfwDestroyWindow(window);
    glfwTerminate();

    AudioEngine::Shutdown();

    std::filesystem::remove(tempScenePath);
}
