#pragma once

#include "Renderable_Base.hpp"

#include <atomic>
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>

#include "renderive/renderable/Renderable_Graph_Builder.hpp"
#include "renderive/scene/base/Scene_Lifetime.hpp"
#include "renderive/state/Render_State_View.hpp"

class Renderable_Base::Impl {
public:
    struct Real_Time_Data_State {
        std::shared_ptr<Scene_Lifetime> scene_lifetime;
        std::atomic<bool> renderable_alive{true};
        std::atomic<bool> attached{};
        std::atomic<bool> discard_stale_frame_on_latest_data_update{};
    };

    using Prepare_Action = std::function<void()>;

    Impl();
    virtual ~Impl();

    void initialize(Renderable_Base& owner,
                    Renderable_Configuration initial_configuration,
                    std::pmr::memory_resource& resource) noexcept;
    void invalidate_prepare() noexcept;
    void invalidate_paint() noexcept;
    void set_configuration(Renderable_Configuration value);
    void set_visible(bool value);
    void discard_stale_frame_on_latest_data_update(bool enabled) noexcept;
    [[nodiscard]] std::pmr::memory_resource& memory_resource() const noexcept;
    [[nodiscard]] Scene_Base& scene() const;
    [[nodiscard]] bool attached() const noexcept;
    void rebuild_render_graph();
    [[nodiscard]] static Render_State_View render_state_view() noexcept;
    [[nodiscard]] Real_Time_Data_Binding bind_real_time_data(
        Real_Time_Data_Base& data);
    void notify_scene_model_dirty() noexcept;
    void bind_scene(std::shared_ptr<Scene_Lifetime> lifetime);
    [[nodiscard]] std::shared_ptr<const Renderable_Graph> render_graph_snapshot();
    void mark_prepared(std::uint64_t revision) noexcept;
    void mark_painted(std::uint64_t paint_revision,
                      std::uint64_t prepare_revision) noexcept;
    void register_real_time_data(Real_Time_Data_Base& data);
    void unregister_real_time_data(Real_Time_Data_Base& data) noexcept;
    void publish_real_time_data();
    void reset_render_graph() noexcept;
    void add_prepare_action(Prepare_Action action);
    void execute_prepare(const Prepare_Render_Context& context);

protected:
    [[nodiscard]] Renderable_Base& owner() noexcept;
    [[nodiscard]] const Renderable_Base& owner() const noexcept;
    virtual void build_prepare_graph(Renderable_Graph_Builder& builder);
    virtual void build_paint_graph(Renderable_Graph_Builder& builder);
    virtual void prepare(const Prepare_Render_Context& context);

private:
    Renderable_Base* owner_{};

public:

    std::pmr::memory_resource* memory_resource_pointer{
        std::pmr::get_default_resource()};
    std::shared_ptr<Real_Time_Data_State> real_time_data_state{
        std::make_shared<Real_Time_Data_State>()};
    const Renderable_Id renderable_id;
    const Render_Node_Id composite_node_id;
    std::atomic<Renderable_Configuration> configuration;
    std::atomic<bool> visible{true};
    std::shared_ptr<const Renderable_Graph> render_graph;
    std::unordered_map<std::string, Render_Node_Id> node_identities;
    std::atomic<std::uint64_t> prepare_revision{1};
    std::atomic<std::uint64_t> prepared_revision{};
    std::atomic<std::uint64_t> paint_revision{1};
    std::atomic<std::uint64_t> painted_revision{};
    std::atomic<std::uint64_t> painted_prepare_revision{};
    std::mutex real_time_data_mutex;
    std::vector<Real_Time_Data_Base*> real_time_data;
    std::vector<Prepare_Action> prepare_actions;
};
