#pragma once
#include <atomic>
#include <cstdint>
#include <memory>
#include <memory_resource>
#include <mutex>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "renderive/base/memory/Memory_Resource.hpp"
#include "renderive/renderable/Renderable_Configuration.hpp"
#include "renderive/renderable/Renderable_Id.hpp"
#include "renderive/renderable/Renderable_Graph_Builder.hpp"
#include "renderive/scene/base/Scene_Lifetime.hpp"
#include "renderive/state/Render_State_View.hpp"
class Frame_Strategy_Real_Time_Data_Observer;
class Real_Time_Data_Base;
class Real_Time_Data_Binding;
class Scene_Base;
class Renderable_Base : public std::enable_shared_from_this<Renderable_Base> {
private:
    struct Real_Time_Data_State {
        explicit Real_Time_Data_State(std::shared_ptr<Scene_Lifetime> scene_lifetime) : scene_lifetime(std::move(scene_lifetime)) {}
        std::shared_ptr<Scene_Lifetime> scene_lifetime;
        std::atomic<bool> renderable_alive{true};
        std::atomic<bool> discard_stale_frame_on_latest_data_update{};
    };
    std::shared_ptr<Scene_Memory_Domain> memory_domain_;
    std::shared_ptr<Real_Time_Data_State> real_time_data_state_;
public:
    explicit Renderable_Base(Scene_Base& scene, Renderable_Configuration configuration = {});
    virtual ~Renderable_Base();
    void invalidate_prepare() noexcept;
    void invalidate_paint() noexcept;
    [[nodiscard]] std::uint64_t prepare_revision() const noexcept;
    [[nodiscard]] std::uint64_t prepared_revision() const noexcept;
    [[nodiscard]] std::uint64_t paint_revision() const noexcept;
    [[nodiscard]] std::uint64_t painted_revision() const noexcept;
    [[nodiscard]] bool prepare_cache_valid() const noexcept;
    [[nodiscard]] bool paint_cache_valid() const noexcept;
    [[nodiscard]] Renderable_Id renderable_id() const noexcept;
    std::pmr::memory_resource& memory_resource() const noexcept;
    Scene_Base& scene() const;
    Renderable_Configuration configuration() const noexcept;
    [[nodiscard]] bool is_visible() const noexcept;
    void set_visible(bool visible);
    std::atomic<bool>& discard_stale_frame_on_latest_data_update;
protected:
    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);
    void rebuild_render_graph() noexcept;
    [[nodiscard]] std::shared_ptr<const Renderable_Graph> 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);
private:
    friend class Frame_Strategy_Real_Time_Data_Observer;
    friend class Real_Time_Data_Binding;
    friend class Scene_Base;
    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();
    const Renderable_Id renderable_id_;
    const Render_Node_Id composite_node_id_;
    mutable std::mutex configuration_mutex_;
    Renderable_Configuration configuration_;
    bool visible_{true};
    std::mutex render_graph_mutex_;
    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_;
};
