#pragma once
#include "../Renderer.hpp"
#include "forward.hpp"

struct windowOptions;
/// Vulkan graphics pipeline
namespace render::vk {
class Allocator;
class SwapChain;
class Pipeline;
class CommandCenter;

/// Vulkan rendering
class Renderer final: public render::Renderer {
public:
    virtual ~Renderer();

    static bool Load(Window& window, const renderOptions& options, const windowOptions& windOpt);

    void loadUI(Window &) override;

    static _FORCE_INLINE_ Renderer *Get() { return static_cast<Renderer*>(render::Renderer::Get()); }

    void beginFrame() override;
    std::function<size_t(render::LodModel *const, glm::mat4, glm::vec4, float)> beginWorldPass(bool solid) override;
    std::function<size_t(render::Model *const, const std::vector<glm::mat4> &)> beginEntityPass() override;
    std::function<size_t(glm::mat4, world::action::Shape, glm::vec4)> beginIndicatorPass() override;
    void postProcess() override;
    void recordUI(std::function<void(VkCommandBuffer)>);
    void endFrame() override;
    void swapBuffer(Window &) override;

    void setClearColor(glm::vec4) override;

    /// Apply camera matrices
    void lookFrom(const Camera &) override;
    void reloadShaders(const passOptions &) override;
    void reloadTextures(const std::string &, float mipMapLOD, int anisotropy) override;
    void setFillMode(bool wireframe) override;
    void setVSync(bool vSync) override;

    Allocator* getAllocator() const { return allocator.get(); }
    struct UICtx {
        VkInstance instance;
        VkPhysicalDevice physicalDevice;
        VkDevice device;
        uint32_t queueFamily;
        VkQueue queue;
        uint32_t imageCount;
        VkRenderPass renderPass;
    };
    UICtx getUICtx() const;

    void setResized() { framebufferResized = true; }

private:
    Renderer(VkInstance, VkDevice, const PhysicalDeviceInfo &, const renderOptions &);

    const PhysicalDeviceInfo &getInfos() const { return *physicalInfo.get(); }

    renderOptions options;

    VkInstance instance;
    VkDebugUtilsMessengerEXT debugMessenger;
    VkSurfaceKHR surface;
    VkDevice device;

    std::unique_ptr<PhysicalDeviceInfo> physicalInfo;
    std::unique_ptr<Allocator> allocator;
    std::unique_ptr<SwapChain> swapChain;
    std::unique_ptr<Pipeline> pipeline;
    std::unique_ptr<CommandCenter> commandCenter;

    size_t currentFrame = 0;
    uint32_t currentImage = UINT32_MAX;
    std::vector<VkSemaphore> imageAvailableSemaphores;
    std::vector<VkSemaphore> renderFinishedSemaphores;
    std::vector<VkFence> inFlightFences;

    bool framebufferResized = false;
    void recreateSwapChain();
    void destroySwapChain();

    glm::mat4 ProjectionMatrix;
    glm::mat4 ViewMatrix;
    float FogDepth;
};
}