#pragma once
#include <cstdint>
#include <mutex>
#include <utility>
#include "renderive/base/Concepts.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 Manual_Refresh_Strategy : public Frame_Control_Strategy_Base {
public:
    enum class Observation_Event {
        prepared,
        prepared_replaced,
        refresh_succeeded,
        refresh_failed,
        rendered,
        real_time_data_updated,
        manually_discarded
    };
    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 render_begin_time_ns{};
        std::uint64_t render_end_time_ns{};
        std::uint64_t render_duration_ns{};
    };
    struct State {
        std::uint64_t prepared_frame_count{};
        std::uint64_t replaced_prepared_frame_count{};
        std::uint64_t discarded_prepared_frame_count{};
        std::uint64_t successful_refresh_count{};
        std::uint64_t failed_refresh_count{};
        std::uint64_t render_count{};
        std::uint64_t real_time_data_update_sequence{};
        std::uint64_t render_frame_sequence{};
        bool pending_frame{};
        bool render_frame{};
    };
    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(Manual_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:
        Manual_Refresh_Strategy* strategy_{};
        Frame* frame_{};
        std::unique_lock<Mutex> lease_lock_;
    };
    class Render_Lease {
    public:
        static constexpr bool thread_affine = true;
        explicit Render_Lease(Manual_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:
        Manual_Refresh_Strategy* strategy_{};
        Frame* frame_{};
        std::unique_lock<Mutex> lease_lock_;
    };
    explicit Manual_Refresh_Strategy(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;
    bool refresh();
    bool discard_pending_frame();
    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;
    Frame frames_[3];
    Observer observer_;
    Frame* paint_;
    Frame* pending_;
    Frame* render_;
    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 painter_mutex_;
    Mutex render_mutex_;
};
#include "Manual_Refresh_Strategy.inl"
