//
// Created by Vicente Ferrari Smith on 13.02.26.
//

#ifndef V_RENDERER_H
#define V_RENDERER_H

#include "metal.h"
#include <GLFW/glfw3.h>
#include "../sprite.h"
#include <misc.h>
#include <simd/simd.h>

static const int kMaxFramesInFlight = 3;

enum class PROJECTION_TYPE : uint8_t {
    NONE,
    ORTHOGRAPHIC_WORLD,
    ORTHOGRAPHIC_WINDOW,
    PERSPECTIVE_WORLD,
    PERSPECTIVE_WINDOW,
    COUNT,
};

// commands

enum class PipelineType : uint8_t {
    None,
    TexturedQuad,
    ColoredQuad,
    Line,
    Text,
    Chunk
};

struct TexturedQuadCmd {
    simd::float2 pos;
    simd::float2 scale;
    simd::float2 uv0;
    simd::float2 uv1;
    simd::float4 colour;
    MTL::Texture *texture;
};

struct ColoredQuadCmd {
    simd::float2 pos;
    simd::float2 scale;
    simd::float4 colour;
};

struct LineCmd {
    simd::float2 start;
    simd::float2 end;
    simd::float4 color;
};

// struct TextCmd {
//     Font* font;
//     std::string text;
//     glm::vec2 position;
//     glm::vec4 color;
// };

//struct ChunkCmd {
//    VkBuffer vertexBuffer;
//    VkBuffer indexBuffer;
//    uint32_t indexCount;
//};

struct SortKey {
    uint16_t depth; // world Z or Y-sorted depth
    uint16_t materialID; // texture sheet, font atlas, etc.
    uint8_t pipeline; // PipelineType

    bool operator<(const SortKey& b) const;
};

struct RenderCommand {

    SortKey      key;
    PipelineType pipeline;

    union {
        TexturedQuadCmd textured_quad;
        ColoredQuadCmd  colored_quad;
        LineCmd         line;
        // TextCmd         text;
        // ChunkCmd        chunk;
    };
};

////////////////////////////////////////////////////////////////////////////////////////////////

// struct AllocatedBuffer {
//     VkBuffer buffer;
//     VmaAllocation allocation;
//     VmaAllocationInfo info;
// };

// struct GPUMeshBuffers {
//     AllocatedBuffer indexBuffer;
//     AllocatedBuffer vertexBuffer;
//     VkDeviceAddress vertexBufferAddress;
// };

struct Renderer {
    std::vector<RenderCommand> commands{};

    MTL::RenderPipelineState *textured_quad_pipeline{};
    MTL::RenderPipelineState *colored_quad_pipeline{};
    MTL::RenderPipelineState *line_pipeline{};
    MTL::RenderPipelineState *text_pipeline{};
    MTL::RenderPipelineState *chunk_pipeline{};

    uint32_t nextTextureSlot = 0;

    MTL::SamplerState  *sampler_state{};
    MTL::CommandBuffer *command_buffer{};

    struct Frame {
        MTL::Buffer *vertex_buffer{};
        MTL::Buffer *uniform_buffer{};
    };

    Frame frames[kMaxFramesInFlight];

    dispatch_semaphore_t frame_semaphore;
    uint8_t current_frame = 0;

//     struct Frame {
//         VkCommandPool commandPool{};
//         VkCommandBuffer command_buffer{};
//
//         VkSemaphore imageAvailable{};
//         VkFence in_flight_fence{};
//
//         AllocatedBuffer vertexBuffer{};
//     };
//
//     std::vector<Frame> frames;
//     uint32_t currentFrame = 0;
//
//     VkDescriptorPool descriptorPool{};
//     std::vector<VkDescriptorSet> textureSets{};
//
    void begin_frame();
    void end_frame(GLFWwindow *window);
    void flush();

    void submit_sprite(simd::float2 pos, const sprite_t &sprite);
    void submit_quad(simd::float2 pos, simd::float2 scale);

    Renderer() = default;
    explicit Renderer(GLFWwindow *window);
    void create_render_pipeline();
    void send_render_command(GLFWwindow *window);
    void encode_render_command(GLFWwindow *window, MTL::RenderCommandEncoder *render_command_encoder);
//     void create_pipeline_layout();
//     void createFrameResources();
//     void create_default_sampler();
//     void recordCommandBuffer(
//         VkCommandBuffer cmd,
//         VkImage image,
//         VkImageView imageView,
//         VkExtent2D extent,
//         VkImageLayout oldLayout,
//         const Frame &frame,
//         const std::vector<vertex_p2_s2_st2_col4_a1_u32> &vertices) const;
//     void immediate_submit(std::function<void(VkCommandBuffer)>&& func) const;
//     void transition_image_layout(VkCommandBuffer cmd, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) const;
//     VkImageView create_image_view(VkImage image, VkFormat format) const;
//     AllocatedBuffer create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage);
//     void destroy_buffer(const AllocatedBuffer& buffer);
//     // GPUMeshBuffers uploadMesh(std::span<uint32_t> indices, std::span<vertex_p2_st2_col4_a1_u32> vertices);
//     void upload_vertex_buffer(
//         VkCommandBuffer cmd,
//         const Frame &frame,
//         std::span<const vertex_p2_s2_st2_col4_a1_u32> vertices) const;
//
    [[nodiscard]] MTL::RenderPipelineState *get_pipeline(PipelineType type) const;
//     // void bind_material(VkCommandBuffer cmd, uint16_t materialID);
//     void create_descriptor_pool();
//     void update_bindless_slot(uint32_t slot, VkImageView view, VkSampler sampler) const;
};

#endif //V_RENDERER_H
