#pragma once
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Painter_Lease::Painter_Lease(Flow_Refresh_Strategy& strategy)
    : strategy_(&strategy) {
    std::lock_guard<Mutex> lock(strategy.state_mutex_);
    frame_ = make_memory_resource_unique<Frame>(*strategy.memory_resource_);
    frame_->statistics.sequence = ++strategy.next_sequence_;
    frame_->statistics.real_time_data_update_sequence = strategy.real_time_data_update_sequence_;
    frame_->statistics.paint_begin_time_ns = strategy.now_ns();
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Painter_Lease::Painter_Lease(Painter_Lease&& other) noexcept
    : strategy_(std::exchange(other.strategy_, nullptr)), frame_(std::move(other.frame_)) {}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Painter_Lease::~Painter_Lease() {
    if (!strategy_) {
        return;
    }
    frame_->statistics.paint_end_time_ns = strategy_->now_ns();
    frame_->statistics.paint_duration_ns = frame_->statistics.paint_end_time_ns - frame_->statistics.paint_begin_time_ns;
    frame_->statistics.queue_enter_time_ns = strategy_->now_ns();
    Observation observation;
    {
        std::lock_guard<Mutex> lock(strategy_->state_mutex_);
        const Statistics statistics = frame_->statistics;
        strategy_->frames_.push_back(std::move(frame_));
        ++strategy_->state_.enqueued_frame_count;
        strategy_->state_.pending_frame_count = strategy_->frames_.size();
        strategy_->update_frame_control_state(Frame_Control_Strategy_Base::invalid_frequency_hz(), 0);
        observation = {Observation_Event::enqueued, statistics, strategy_->state_, strategy_->last_real_time_data_update_};
    }
    strategy_->observe(observation);
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Painter_Lease::operator bool() const {
    return frame_ != nullptr;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Painter_Lease::operator->() -> Frame* {
    return frame_.get();
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Painter_Lease::operator->() const -> const Frame* {
    return frame_.get();
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Painter_Lease::operator*() -> Frame& {
    return *frame_;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Painter_Lease::operator*() const -> const Frame& {
    return *frame_;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Painter_Lease::get() -> Frame* {
    return frame_.get();
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Painter_Lease::get() const -> const Frame* {
    return frame_.get();
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Render_Lease::Render_Lease(Flow_Refresh_Strategy& strategy)
    : strategy_(&strategy) {
    if (rendering_strategy_ == &strategy) {
        strategy_ = nullptr;
        return;
    }
    lease_lock_ = std::unique_lock<Mutex>(strategy.render_mutex_);
    previous_render_strategy_ = std::exchange(rendering_strategy_, &strategy);
    Observation observation;
    bool has_frame{};
    {
        std::lock_guard<Mutex> lock(strategy.state_mutex_);
        if (!strategy.frames_.empty()) {
            frame_ = std::move(strategy.frames_.front());
            strategy.frames_.pop_front();
            frame_->statistics.queue_leave_time_ns = strategy.now_ns();
            frame_->statistics.queue_wait_ns = frame_->statistics.queue_leave_time_ns - frame_->statistics.queue_enter_time_ns;
            frame_->statistics.render_begin_time_ns = strategy.now_ns();
            ++strategy.state_.dequeued_frame_count;
            strategy.state_.pending_frame_count = strategy.frames_.size();
            strategy.update_frame_control_state(Frame_Control_Strategy_Base::invalid_frequency_hz(), 0);
            observation = {Observation_Event::dequeued, frame_->statistics, strategy.state_, strategy.last_real_time_data_update_};
            has_frame = true;
        } else {
            ++strategy.state_.empty_acquire_count;
            strategy.state_.pending_frame_count = 0;
            strategy.update_frame_control_state(Frame_Control_Strategy_Base::invalid_frequency_hz(), 0);
            observation = {Observation_Event::queue_empty, {}, strategy.state_, strategy.last_real_time_data_update_};
        }
    }
    if (!has_frame) {
        lease_lock_.unlock();
        rendering_strategy_ = previous_render_strategy_;
    }
    strategy.observe(observation);
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Render_Lease::Render_Lease(Render_Lease&& other) noexcept
    : strategy_(std::exchange(other.strategy_, nullptr)), previous_render_strategy_(other.previous_render_strategy_), frame_(std::move(other.frame_)), lease_lock_(std::move(other.lease_lock_)) {}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Render_Lease::~Render_Lease() {
    if (!strategy_ || !frame_) {
        return;
    }
    frame_->statistics.render_end_time_ns = strategy_->now_ns();
    frame_->statistics.render_duration_ns = frame_->statistics.render_end_time_ns - frame_->statistics.render_begin_time_ns;
    Observation observation;
    {
        std::lock_guard<Mutex> lock(strategy_->state_mutex_);
        ++strategy_->state_.rendered_frame_count;
        strategy_->state_.pending_frame_count = strategy_->frames_.size();
        strategy_->update_frame_control_state(Frame_Control_Strategy_Base::invalid_frequency_hz(), 0);
        observation = {Observation_Event::rendered, frame_->statistics, strategy_->state_, strategy_->last_real_time_data_update_};
        frame_.reset();
    }
    lease_lock_.unlock();
    rendering_strategy_ = previous_render_strategy_;
    strategy_->observe(observation);
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Render_Lease::operator bool() const {
    return frame_ != nullptr;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Render_Lease::operator->() -> Frame* {
    return frame_.get();
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Render_Lease::operator->() const -> const Frame* {
    return frame_.get();
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Render_Lease::operator*() -> Frame& {
    return *frame_;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Render_Lease::operator*() const -> const Frame& {
    return *frame_;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Render_Lease::get() -> Frame* {
    return frame_.get();
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Render_Lease::get() const -> const Frame* {
    return frame_.get();
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Flow_Refresh_Strategy(Observer observer)
    : Flow_Refresh_Strategy(*std::pmr::get_default_resource(), std::move(observer)) {}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Flow_Refresh_Strategy(std::pmr::memory_resource& memory_resource, Observer observer)
    : Frame_Control_Strategy_Base(), memory_resource_(&memory_resource), observer_(std::move(observer)), frames_(&memory_resource) {}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::acquire_painter() -> Painter_Lease {
    return Painter_Lease(*this);
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::acquire_renderer() -> Render_Lease {
    return Render_Lease(*this);
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
void Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::swap() {
    swap_frame_control_state();
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
double Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::frequency_hz() const noexcept {
    return render_frame_control_state().frequency_hz;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
std::uint64_t Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::next_refresh_interval_ns() const noexcept {
    return render_frame_control_state().next_refresh_interval_ns;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::frame_control_state() const -> Frame_Control_Strategy_Base::State {
    return render_frame_control_state();
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
std::size_t Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::pending_frame_count() const {
    std::lock_guard<Mutex> lock(state_mutex_);
    return frames_.size();
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::state() const -> State {
    std::lock_guard<Mutex> lock(state_mutex_);
    State result = state_;
    result.pending_frame_count = frames_.size();
    return result;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
void Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::on_real_time_data_update(const Real_Time_Data_Observation& observation) noexcept {
    Observation strategy_observation;
    {
        std::lock_guard<Mutex> lock(state_mutex_);
        ++real_time_data_update_sequence_;
        state_.real_time_data_update_sequence = real_time_data_update_sequence_;
        last_real_time_data_update_ = observation.state;
        update_frame_control_state(Frame_Control_Strategy_Base::invalid_frequency_hz(), 0);
        strategy_observation = {Observation_Event::real_time_data_updated, {}, state_, last_real_time_data_update_};
    }
    observe(strategy_observation);
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
std::uint64_t Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::now_ns() const noexcept {
    return observer_.now_ns();
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
void Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::observe(const Observation& observation) noexcept {
    observer_.observe(observation);
}
