#pragma once

#include <Core/Container/Vector.h>
#include <Core/Math/Matrix.h>
#include <Graphics/Lighting.h>
#include <Graphics/PushConstants.h>
#include <Graphics/VertexData.h>
#include <Juliet.h>

namespace Juliet
{
    struct GraphicsTransferBuffer;
    struct RenderPass;
    struct CommandList;
    struct GraphicsBuffer;
    struct Window;
    struct GraphicsPipeline;
    struct GraphicsDevice;
    struct Mesh;

    using MeshID  = index_t;
    using LightID = index_t;

    constexpr size_t kGeometryPage       = Megabytes(64);
    constexpr size_t kIndexPage          = Megabytes(32);
    constexpr size_t kDefaultMeshNumber  = 500;
    constexpr size_t kDefaultVertexCount = 2'000'000;  // Fit less than one geometry page
    constexpr size_t kDefaultIndexCount  = 16'000'000; // Fit less than one index page
    constexpr size_t kDefaultLightCount  = 1024;

    struct MeshRenderer
    {
        VectorArena<Mesh, kDefaultMeshNumber>       Meshes;
        VectorArena<Vertex, kDefaultVertexCount>    Vertices;
        VectorArena<Index, kDefaultIndexCount>      Indices;
        VectorArena<PointLight, kDefaultLightCount> PointLights;

        GraphicsBuffer*         VertexBuffer;
        GraphicsBuffer*         IndexBuffer;
        GraphicsTransferBuffer* StreamCopyBuffer;
        GraphicsTransferBuffer* LoadCopyBuffer;

        GraphicsBuffer*         LightsBuffer;
        PointLight*             MappedLights;

        GraphicsDevice*   Device;
        GraphicsPipeline* Pipeline;
    };

    JULIET_API void InitializeMeshRenderer(NonNullPtr<Arena> arena);
    [[nodiscard]] JULIET_API bool InitializeMeshRendererGraphics(NonNullPtr<GraphicsDevice> device, NonNullPtr<Window> window);
    JULIET_API void ShutdownMeshRendererGraphics();
    JULIET_API void ShutdownMeshRenderer();
    JULIET_API void LoadMeshesOnGPU(NonNullPtr<CommandList> cmdList);
    JULIET_API void RenderMeshes(NonNullPtr<RenderPass> pass, NonNullPtr<CommandList> cmdList, PushData& pushData);

    // Lights
    [[nodiscard]] JULIET_API LightID AddPointLight(const PointLight& light);
    JULIET_API void                  SetPointLightPosition(LightID id, const Vector3& position);
    JULIET_API void                  SetPointLightColor(LightID id, const Vector3& color);
    JULIET_API void                  SetPointLightRadius(LightID id, float radius);
    JULIET_API void                  SetPointLightIntensity(LightID id, float intensity);
    JULIET_API void                  ClearPointLights();

    // Utils
    [[nodiscard]] JULIET_API MeshID AddCube();
    [[nodiscard]] JULIET_API MeshID AddQuad();
    JULIET_API void                 SetMeshTransform(MeshID id, const Matrix& transform);

#if ALLOW_SHADER_HOT_RELOAD
    JULIET_API void ReloadMeshRendererShaders();
#endif

} // namespace Juliet
