#pragma once
#include <cstddef>
#include <cstdint>
#include <deque>
#include <memory_resource>
#include <mutex>
#include <utility>
#include "renderive/base/Concepts.hpp"
#include "renderive/base/memory/Memory_Resource.hpp"
#include "renderive/base/observer/Observer.hpp"
#include "renderive/frame_control/base/Frame_Control_Strategy_Base.hpp"
#include "renderive/real_time_data/Observation.hpp"
template <class Scene_Frame, Mutex_Type Mutex = std::mutex, class Observer = Observer_State<>>
class Flow_Refresh_Strategy : public Frame_Control_Strategy_Base {
public:
    enum class Observation_Event {
        enqueued,
        dequeued,
        queue_empty,
        rendered,
        real_time_data_updated
    };
    struct Statistics {
        std::uint64_t sequence{};
        std::uint64_t real_time_data_update_sequence{};
        std::uint64_t paint_begin_time_ns{};
        std::uint64_t paint_end_time_ns{};
        std::uint64_t paint_duration_ns{};
        std::uint64_t queue_enter_time_ns{};
        std::uint64_t queue_leave_time_ns{};
        std::uint64_t queue_wait_ns{};
        std::uint64_t render_begin_time_ns{};
        std::uint64_t render_end_time_ns{};
        std::uint64_t render_duration_ns{};
    };
    struct State {
        std::uint64_t enqueued_frame_count{};
        std::uint64_t dequeued_frame_count{};
        std::uint64_t rendered_frame_count{};
        std::uint64_t empty_acquire_count{};
        std::uint64_t real_time_data_update_sequence{};
        std::size_t pending_frame_count{};
    };
    struct Observation {
        Observation_Event event{};
        Statistics statistics;
        State state;
        Real_Time_Data_Update_State real_time_data;
    };
    struct Frame : Scene_Frame {
        Statistics statistics;
    };
    static_assert(Timed_Struct_Observer<Observer, Observation>);
    class Painter_Lease {
    public:
        static constexpr bool thread_affine = true;
        explicit Painter_Lease(Flow_Refresh_Strategy& strategy);
        Painter_Lease(const Painter_Lease&) = delete;
        Painter_Lease& operator=(const Painter_Lease&) = delete;
        Painter_Lease(Painter_Lease&& other) noexcept;
        Painter_Lease& operator=(Painter_Lease&&) = delete;
        ~Painter_Lease();
        explicit operator bool() const;
        Frame* operator->();
        const Frame* operator->() const;
        Frame& operator*();
        const Frame& operator*() const;
        Frame* get();
        const Frame* get() const;
    private:
        Flow_Refresh_Strategy* strategy_{};
        Memory_Resource_Unique_Ptr<Frame> frame_;
    };
    class Render_Lease {
    public:
        static constexpr bool thread_affine = true;
        explicit Render_Lease(Flow_Refresh_Strategy& strategy);
        Render_Lease(const Render_Lease&) = delete;
        Render_Lease& operator=(const Render_Lease&) = delete;
        Render_Lease(Render_Lease&& other) noexcept;
        Render_Lease& operator=(Render_Lease&&) = delete;
        ~Render_Lease();
        explicit operator bool() const;
        Frame* operator->();
        const Frame* operator->() const;
        Frame& operator*();
        const Frame& operator*() const;
        Frame* get();
        const Frame* get() const;
    private:
        Flow_Refresh_Strategy* strategy_{};
        Flow_Refresh_Strategy* previous_render_strategy_{};
        Memory_Resource_Unique_Ptr<Frame> frame_;
        std::unique_lock<Mutex> lease_lock_;
    };
    explicit Flow_Refresh_Strategy(Observer observer = {});
    Flow_Refresh_Strategy(std::pmr::memory_resource& memory_resource, Observer observer = {});
    Painter_Lease acquire_painter();
    Render_Lease acquire_renderer();
    void swap() override;
    double frequency_hz() const noexcept override;
    std::uint64_t next_refresh_interval_ns() const noexcept override;
    Frame_Control_Strategy_Base::State frame_control_state() const override;
    std::size_t pending_frame_count() const;
    State state() const;
    void on_real_time_data_update(const Real_Time_Data_Observation& observation) noexcept override;
private:
    std::uint64_t now_ns() const noexcept;
    void observe(const Observation& observation) noexcept;
    inline static thread_local Flow_Refresh_Strategy* rendering_strategy_{};
    std::pmr::memory_resource* const memory_resource_;
    Observer observer_;
    std::pmr::deque<Memory_Resource_Unique_Ptr<Frame>> frames_;
    std::uint64_t next_sequence_{};
    std::uint64_t real_time_data_update_sequence_{};
    Real_Time_Data_Update_State last_real_time_data_update_;
    State state_;
    mutable Mutex state_mutex_;
    Mutex render_mutex_;
};
#include "Flow_Refresh_Strategy.inl"
