#pragma once
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Painter_Lease::Painter_Lease(Manual_Refresh_Strategy& strategy)
    : strategy_(&strategy), lease_lock_(strategy.painter_mutex_) {
    std::lock_guard<Mutex> lock(strategy.state_mutex_);
    frame_ = strategy.paint_;
    frame_->statistics = {};
    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>
Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Painter_Lease::Painter_Lease(Painter_Lease&& other) noexcept
    : strategy_(std::exchange(other.strategy_, nullptr)), frame_(std::exchange(other.frame_, nullptr)), lease_lock_(std::move(other.lease_lock_)) {}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
Manual_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;
    Observation replaced_observation;
    Observation prepared_observation;
    bool replaced{};
    {
        std::lock_guard<Mutex> lock(strategy_->state_mutex_);
        if (strategy_->state_.pending_frame) {
            ++strategy_->state_.replaced_prepared_frame_count;
            replaced_observation = {Observation_Event::prepared_replaced, strategy_->pending_->statistics, strategy_->state_, strategy_->last_real_time_data_update_};
            replaced = true;
        }
        std::swap(strategy_->paint_, strategy_->pending_);
        strategy_->state_.pending_frame = true;
        ++strategy_->state_.prepared_frame_count;
        strategy_->update_frame_control_state(Frame_Control_Strategy_Base::invalid_frequency_hz(), 0);
        prepared_observation = {Observation_Event::prepared, frame_->statistics, strategy_->state_, strategy_->last_real_time_data_update_};
    }
    lease_lock_.unlock();
    if (replaced) {
        strategy_->observe(replaced_observation);
    }
    strategy_->observe(prepared_observation);
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Painter_Lease::operator bool() const {
    return frame_ != nullptr;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Painter_Lease::operator->() -> Frame* {
    return frame_;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Painter_Lease::operator->() const -> const Frame* {
    return frame_;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Painter_Lease::operator*() -> Frame& {
    return *frame_;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Painter_Lease::operator*() const -> const Frame& {
    return *frame_;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Painter_Lease::get() -> Frame* {
    return frame_;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Painter_Lease::get() const -> const Frame* {
    return frame_;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Render_Lease::Render_Lease(Manual_Refresh_Strategy& strategy)
    : strategy_(&strategy), lease_lock_(strategy.render_mutex_) {
    std::lock_guard<Mutex> lock(strategy.state_mutex_);
    frame_ = strategy.state_.render_frame ? strategy.render_ : nullptr;
    if (frame_) {
        frame_->statistics.render_begin_time_ns = strategy.now_ns();
    }
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Render_Lease::Render_Lease(Render_Lease&& other) noexcept
    : strategy_(std::exchange(other.strategy_, nullptr)), frame_(std::exchange(other.frame_, nullptr)), lease_lock_(std::move(other.lease_lock_)) {}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
Manual_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_.render_count;
        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_};
    }
    lease_lock_.unlock();
    strategy_->observe(observation);
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Render_Lease::operator bool() const {
    return frame_ != nullptr;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Render_Lease::operator->() -> Frame* {
    return frame_;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Render_Lease::operator->() const -> const Frame* {
    return frame_;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Render_Lease::operator*() -> Frame& {
    return *frame_;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Render_Lease::operator*() const -> const Frame& {
    return *frame_;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Render_Lease::get() -> Frame* {
    return frame_;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Render_Lease::get() const -> const Frame* {
    return frame_;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Manual_Refresh_Strategy(Observer observer)
    : Frame_Control_Strategy_Base(), observer_(std::move(observer)), paint_(&frames_[0]), pending_(&frames_[1]), render_(&frames_[2]) {}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::acquire_painter() -> Painter_Lease {
    return Painter_Lease(*this);
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::acquire_renderer() -> Render_Lease {
    return Render_Lease(*this);
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
void Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::swap() {
    swap_frame_control_state();
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
double Manual_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 Manual_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 Manual_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>
bool Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::refresh() {
    std::unique_lock<Mutex> render_lock(render_mutex_);
    Observation observation;
    bool refreshed{};
    {
        std::lock_guard<Mutex> lock(state_mutex_);
        if (state_.pending_frame) {
            std::swap(pending_, render_);
            state_.pending_frame = false;
            state_.render_frame = true;
            state_.render_frame_sequence = render_->statistics.sequence;
            ++state_.successful_refresh_count;
            update_frame_control_state(Frame_Control_Strategy_Base::invalid_frequency_hz(), 0);
            observation = {Observation_Event::refresh_succeeded, render_->statistics, state_, last_real_time_data_update_};
            refreshed = true;
        } else {
            ++state_.failed_refresh_count;
            update_frame_control_state(Frame_Control_Strategy_Base::invalid_frequency_hz(), 0);
            observation = {Observation_Event::refresh_failed, {}, state_, last_real_time_data_update_};
        }
    }
    render_lock.unlock();
    observe(observation);
    return refreshed;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
bool Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::discard_pending_frame() {
    Observation observation;
    {
        std::lock_guard<Mutex> lock(state_mutex_);
        if (!state_.pending_frame) {
            return false;
        }
        state_.pending_frame = false;
        ++state_.discarded_prepared_frame_count;
        update_frame_control_state(Frame_Control_Strategy_Base::invalid_frequency_hz(), 0);
        observation = {Observation_Event::manually_discarded, pending_->statistics, state_, last_real_time_data_update_};
    }
    observe(observation);
    return true;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
auto Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::state() const -> State {
    std::lock_guard<Mutex> lock(state_mutex_);
    return state_;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
void Manual_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 Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::now_ns() const noexcept {
    return observer_.now_ns();
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
void Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::observe(const Observation& observation) noexcept {
    observer_.observe(observation);
}
