#pragma once
#include <cmath>
#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/frame_control/Frame_Consumer_Feedback.hpp"
#include "renderive/real_time_data/Observation.hpp"
template <class Scene_Frame, Mutex_Type Mutex = std::mutex, class Observer = Observer_State<>>
class Low_Latency_Strategy : public Frame_Control_Strategy_Base {
public:
    enum class Observation_Event {
        published,
        abandoned,
        manually_discarded,
        swap_failed,
        rendered,
        lifecycle_completed,
        real_time_data_updated
    };
    enum class Limit_State {
        frequency_limited,
        paint_limited,
        render_limited,
        consumer_limited,
        unlimited
    };
    struct Configuration {
        double frequency_hz{60.0};
    };
    struct Timing_Statistics {
        std::uint64_t paint_request_time_ns{};
        std::uint64_t paint_lease_acquired_time_ns{};
        std::uint64_t paint_begin_time_ns{};
        std::uint64_t paint_end_time_ns{};
        std::uint64_t publish_time_ns{};
        std::uint64_t render_request_time_ns{};
        std::uint64_t render_lease_acquired_time_ns{};
        std::uint64_t render_begin_time_ns{};
        std::uint64_t render_end_time_ns{};
        std::uint64_t paint_lease_wait_ns{};
        std::uint64_t paint_state_wait_ns{};
        std::uint64_t paint_duration_ns{};
        std::uint64_t publish_state_wait_ns{};
        std::uint64_t ready_wait_ns{};
        std::uint64_t frame_age_at_render_ns{};
        std::uint64_t render_lease_wait_ns{};
        std::uint64_t render_state_wait_ns{};
        std::uint64_t render_duration_ns{};
        std::uint64_t render_finish_state_wait_ns{};
        std::uint64_t end_to_end_ns{};
    };
    struct Counter_Statistics {
        std::uint64_t published_frame_count{};
        std::uint64_t render_pass_count{};
        std::uint64_t abandoned_frame_count{};
        std::uint64_t manually_discarded_frame_count{};
        std::uint64_t swap_failure_count{};
    };
    struct Statistics {
        std::uint64_t sequence{};
        std::uint64_t frame_render_count{};
        std::uint64_t real_time_data_update_sequence{};
        Timing_Statistics timing;
        Counter_Statistics counters;
    };
    struct State {
        double frequency_hz{60.0};
        Limit_State limit_state{Limit_State::frequency_limited};
        bool frequency_limit_enabled{true};
        bool consumer_feedback_enabled{};
        std::uint64_t target_interval_ns{};
        std::uint64_t paint_duration_ns{};
        std::uint64_t render_duration_ns{};
        std::uint64_t bottleneck_duration_ns{};
        std::uint64_t consumer_sample_interval_ns{};
        std::uint64_t consumer_smoothed_interval_ns{};
        std::uint64_t consumer_variation_ns{};
        std::uint64_t consumer_safety_interval_ns{};
        std::uint64_t consumer_interval_ns{};
        std::uint64_t end_to_end_ns{};
        std::uint64_t next_refresh_interval_ns{};
        std::uint64_t completed_lifecycle_count{};
        std::uint64_t frame_sequence{};
        std::uint64_t real_time_data_update_sequence{};
        std::uint64_t completed_frame_real_time_data_update_sequence{};
    };
    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(Low_Latency_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:
        Low_Latency_Strategy* strategy_{};
        Frame* frame_{};
        std::uint64_t request_time_ns_{};
        std::unique_lock<Mutex> lease_lock_;
    };
    class Render_Lease {
    public:
        static constexpr bool thread_affine = true;
        explicit Render_Lease(Low_Latency_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:
        Low_Latency_Strategy* strategy_{};
        Frame* frame_{};
        std::uint64_t request_time_ns_{};
        bool acquired_new_frame_{};
        bool swap_failed_{};
        Observation swap_failed_observation_;
        std::unique_lock<Mutex> lease_lock_;
    };
    explicit Low_Latency_Strategy(Observer observer = {}, Configuration configuration = {});
    Low_Latency_Strategy(const Low_Latency_Strategy&) = delete;
    Low_Latency_Strategy& operator=(const Low_Latency_Strategy&) = delete;
    Low_Latency_Strategy(Low_Latency_Strategy&&) = delete;
    Low_Latency_Strategy& operator=(Low_Latency_Strategy&&) = delete;
    Painter_Lease acquire_painter();
    Render_Lease acquire_renderer();
    void set_frequency_hz(double frequency_hz);
    void clear_frequency_limit();
    void set_consumer_feedback(Frame_Consumer_Feedback feedback);
    void clear_consumer_feedback();
    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;
    State state() const;
    Counter_Statistics counter_statistics() const;
    void on_real_time_data_update(const Real_Time_Data_Observation& observation) noexcept override;
    bool discard_pending_frame();
    bool discard_pending_frame_before(std::uint64_t real_time_data_update_sequence);
    bool discard_stale_latest_data_frame() noexcept override;
private:
    static double checked_frequency_hz(double frequency_hz);
    std::uint64_t now_ns() const noexcept;
    void update_refresh_control();
    void update_consumer_estimator(std::uint64_t sample_interval_ns);
    void update_state(const Frame& frame);
    void observe(const Observation& observation) noexcept;
    bool discard_pending_frame_locked(Observation& observation);
    Frame frames_[3];
    Observer observer_;
    State state_;
    Frame* paint_;
    Frame* cache_;
    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_;
    Counter_Statistics counters_;
    bool cache_ready_{};
    bool render_ready_{};
    mutable Mutex state_mutex_;
    Mutex painter_mutex_;
    Mutex render_mutex_;
};
#include "Low_Latency_Strategy.inl"
