#include <Core/Common/CoreUtils.h>
#include <Core/HAL/Filesystem/Filesystem_Private.h>
#include <Core/JulietInit.h>
#include <Core/Logging/LogManager.h>
#include <Core/Memory/MemoryArena.h>
#include <Engine/Engine.h>
#include <Graphics/DebugDisplay.h>
#include <Graphics/Graphics.h>
#include <Graphics/MeshRenderer.h>
#include <Graphics/RenderPass.h>
#include <Graphics/SkyboxRenderer.h>
#include <time.h>

#ifdef JULIET_ENABLE_IMGUI
#include <Core/ImGui/ImGuiService.h>
#include <Graphics/ImGuiRenderer.h>
#include <imgui.h>
#endif

namespace Juliet
{
#if JULIET_DEBUG
    namespace UnitTest
    {
        extern void RunUnitTests();
    }
#endif

    namespace
    {
        Engine EngineInstance;

        // Initialize systems that depend on graphics/window (after App::Init)
        void InitializeDependentSystems()
        {
            GraphicsDevice* device = EngineInstance.Application->GetGraphicsDevice();

            // DebugDisplay system
            if (device)
            {
                DebugDisplay_Initialize(device);
                if (Window* window = EngineInstance.Application->GetPlatformWindow())
                {
                    bool success = InitializeMeshRendererGraphics(device, window);
                    Assert(success);
                    (void)(success);

                    success = InitializeSkyboxRenderer(device, window);
                    Assert(success);
                    (void)(success);
                }
            }

#ifdef JULIET_ENABLE_IMGUI
            if (NonNullPtr window = EngineInstance.Application->GetPlatformWindow())
            {
                ImGuiService::Initialize(window);
                ImGui::SetCurrentContext(ImGuiService::GetContext());

                if (device)
                {
                    ImGuiRenderer_Initialize(device);

                    // Run Unit Tests automatically
                    ImGuiService::RunTests();
                }
            }
#endif
        }

        // Shutdown systems that were initialized in InitializeDependentSystems
        void ShutdownDependentSystems()
        {
            GraphicsDevice* device = EngineInstance.Application->GetGraphicsDevice();

#ifdef JULIET_ENABLE_IMGUI
            if (device)
            {
                ImGuiRenderer_Shutdown(device);
            }
            ImGuiService::Shutdown();
#endif

            // DebugDisplay system
            if (device)
            {
                DebugDisplay_Shutdown(device);
                ShutdownSkyboxRenderer();
                ShutdownMeshRendererGraphics();
            }
        }

        // Render one frame
        void RenderFrame()
        {
            GraphicsDevice* device = EngineInstance.Application->GetGraphicsDevice();
            Window*         window = EngineInstance.Application->GetPlatformWindow();

            if (!device || !window)
            {
                return;
            }

            CommandList* cmdList = AcquireCommandList(device, QueueType::Graphics);
            if (!cmdList)
            {
                return;
            }

            Texture* swapChainTexture = nullptr;
            if (!WaitAndAcquireSwapChainTexture(cmdList, window, &swapChainTexture))
            {
                // Swapchain might need resize, submit empty and try again next frame
                SubmitCommandLists(cmdList);
                return;
            }

            if (swapChainTexture)
            {
                // Pre-render phase (buffer uploads, etc.)
                EngineInstance.Application->OnPreRender(cmdList);

                // Prepare debug display data (before render pass)
                DebugDisplay_Prepare(cmdList);

                // Get render targets from application
                ColorTargetInfo         colorInfo = EngineInstance.Application->GetColorTargetInfo(swapChainTexture);
                DepthStencilTargetInfo* depthInfo = EngineInstance.Application->GetDepthTargetInfo();

                RenderPass* pass = BeginRenderPass(cmdList, colorInfo, depthInfo);

                // Application rendering
                EngineInstance.Application->OnRender(pass, cmdList);

                // Debug display flush (inside render pass)
                Camera debugCamera = EngineInstance.Application->GetDebugCamera();
                DebugDisplay_Flush(cmdList, pass, debugCamera);

                // Note: The MeshRenderer and SkyboxRenderer draw calls are still inside Application->OnRender
                // They shouldn't be moved here directly without an interface since they require PushData.

#ifdef JULIET_ENABLE_IMGUI
                // ImGui rendering (always last before EndRenderPass)
                ImGuiRenderer_Render(cmdList, pass);
#endif

                EndRenderPass(pass);
            }

            SubmitCommandLists(cmdList);
        }
    } // namespace

    void InitializeEngine(JulietInit_Flags flags)
    {
        EngineInstance.PlatformArena = ArenaAllocate({ .AllowRealloc = true } JULIET_DEBUG_PARAM("Platform Arena"));

        InitializeLogManager();

#if JULIET_DEBUG
        UnitTest::RunUnitTests();
#endif

        InitFilesystem();

        JulietInit(flags);
    }

    void ShutdownEngine()
    {
        JulietShutdown();

        ShutdownFilesystem();
        ShutdownLogManager();

        ArenaRelease(EngineInstance.PlatformArena);
    }

    void LoadApplication(IApplication& app)
    {
        EngineInstance.Application = &app;

        InitializeMeshRenderer(EngineInstance.PlatformArena);

        EngineInstance.Application->Init(EngineInstance.PlatformArena);

        // Systems depending on Window/GraphicsDevice
        InitializeDependentSystems();
    }

    void UnloadApplication()
    {
        // Shutdown dependent systems before app shutdown
        ShutdownDependentSystems();

        EngineInstance.Application->Shutdown();

        ShutdownMeshRenderer();

        EngineInstance.Application = nullptr;
    }

    void RunEngine()
    {
        while (EngineInstance.Application->IsRunning())
        {
#ifdef JULIET_ENABLE_IMGUI
            ImGuiRenderer_NewFrame();
#endif

            // Logic tick
            EngineInstance.Application->Update();

            // Render tick
            RenderFrame();
        }
    }
} // namespace Juliet
