#pragma once
template <class Value_Type, class Container, Mutex_Type Mutex, class Observer>
History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::History_Real_Time_Data(Observer observer)
    : History_Real_Time_Data(*std::pmr::get_default_resource(), std::move(observer)) {}
template <class Value_Type, class Container, Mutex_Type Mutex, class Observer>
History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::History_Real_Time_Data(std::pmr::memory_resource& memory_resource, Observer observer)
    : observer_(std::move(observer)),
      states_{make_container(memory_resource), make_container(memory_resource), make_container(memory_resource)},
      render_state_(&states_[0]), cache_state_(&states_[1]), scratch_state_(&states_[2]),
      memory_resource_(&memory_resource) {}
template <class Value_Type, class Container, Mutex_Type Mutex, class Observer>
History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::~History_Real_Time_Data() {
    if (update_times_)
        memory_resource_->deallocate(update_times_, update_times_capacity_ * sizeof(std::uint64_t), alignof(std::uint64_t));
}
template <class Value_Type, class Container, Mutex_Type Mutex, class Observer>
auto History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::make_container(std::pmr::memory_resource& memory_resource) -> Container {
    if constexpr (requires { typename Container::allocator_type; }) {
        using Allocator = typename Container::allocator_type;
        if constexpr (std::constructible_from<Allocator, std::pmr::memory_resource*> && std::constructible_from<Container, Allocator>) {
            return Container(Allocator(&memory_resource));
        } else {
            return Container();
        }
    } else {
        return Container();
    }
}
template <class Value_Type, class Container, Mutex_Type Mutex, class Observer>
void History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::reserve_update_times(std::size_t capacity) {
    if (capacity <= update_times_capacity_)
        return;
    const std::size_t next_capacity = std::max(capacity, std::max<std::size_t>(8, update_times_capacity_ * 2));
    auto* next = static_cast<std::uint64_t*>(
        memory_resource_->allocate(next_capacity * sizeof(std::uint64_t), alignof(std::uint64_t)));
    if (update_times_size_ != 0)
        std::copy_n(update_times_, update_times_size_, next);
    if (update_times_)
        memory_resource_->deallocate(update_times_, update_times_capacity_ * sizeof(std::uint64_t), alignof(std::uint64_t));
    update_times_ = next;
    update_times_capacity_ = next_capacity;
}
template <class Value_Type, class Container, Mutex_Type Mutex, class Observer>
void History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::update(Value value) {
    update(std::move(value), std::numeric_limits<std::size_t>::max());
}
template <class Value_Type, class Container, Mutex_Type Mutex, class Observer>
void History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::update(Value value, std::size_t retain_latest_count) {
    std::lock_guard mutation_lock(mutation_mutex_);
    Real_Time_Data_Observation observation;
    {
        std::lock_guard<Mutex> lock(mutex_);
        reserve_update_times(update_times_size_ + 1);
        cache_state_->push_back(std::move(value));
        const std::uint64_t update_time_ns = observer_.now_ns();
        update_times_[update_times_size_++] = update_time_ns;
        if(cache_state_->size() > retain_latest_count) {
            const std::size_t discarded = cache_state_->size() - retain_latest_count;
            cache_state_->erase(cache_state_->begin(), std::next(cache_state_->begin(), static_cast<std::ptrdiff_t>(discarded)));
            std::move(update_times_ + discarded, update_times_ + update_times_size_, update_times_);
            update_times_size_ -= discarded;
        }
        ++revision_;
        ++total_update_count_;
        last_update_time_ns_ = update_time_ns;
        observation = {Real_Time_Data_Observation_Event::updated, {this, Real_Time_Data_Retention::history, revision_, last_update_time_ns_, total_update_count_, cache_state_->size()}};
    }
    observer_.observe(observation);
}
template <class Value_Type, class Container, Mutex_Type Mutex, class Observer>
void History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::clear() {
    std::lock_guard mutation_lock(mutation_mutex_);
    Real_Time_Data_Observation observation;
    {
        std::lock_guard<Mutex> lock(mutex_);
        cache_state_->clear();
        update_times_size_ = 0;
        ++revision_;
        last_update_time_ns_ = observer_.now_ns();
        observation = {Real_Time_Data_Observation_Event::cleared, {this, Real_Time_Data_Retention::history, revision_, last_update_time_ns_, total_update_count_, 0}};
    }
    observer_.observe(observation);
}
template <class Value_Type, class Container, Mutex_Type Mutex, class Observer>
std::size_t History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::retain_latest(std::size_t count) {
    std::lock_guard mutation_lock(mutation_mutex_);
    Real_Time_Data_Observation observation;
    std::size_t discarded{};
    {
        std::lock_guard<Mutex> lock(mutex_);
        if(cache_state_->size() <= count)
            return 0;
        discarded = cache_state_->size() - count;
        cache_state_->erase(cache_state_->begin(), std::next(cache_state_->begin(), static_cast<std::ptrdiff_t>(discarded)));
        std::move(update_times_ + discarded, update_times_ + update_times_size_, update_times_);
        update_times_size_ -= discarded;
        ++revision_;
        observation = {Real_Time_Data_Observation_Event::discarded, {this, Real_Time_Data_Retention::history, revision_, last_update_time_ns_, total_update_count_, cache_state_->size()}};
    }
    observer_.observe(observation);
    return discarded;
}
template <class Value_Type, class Container, Mutex_Type Mutex, class Observer>
auto History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::snapshot() const -> Container {
    std::lock_guard<Mutex> lock(mutex_);
    return *cache_state_;
}
template <class Value_Type, class Container, Mutex_Type Mutex, class Observer>
std::size_t History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::size() const {
    std::lock_guard<Mutex> lock(mutex_);
    return cache_state_->size();
}
template <class Value_Type, class Container, Mutex_Type Mutex, class Observer>
std::uint64_t History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::revision() const {
    std::lock_guard<Mutex> lock(mutex_);
    return revision_;
}
template <class Value_Type, class Container, Mutex_Type Mutex, class Observer>
Real_Time_Data_Update_State History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::update_state() const {
    std::lock_guard<Mutex> lock(mutex_);
    return {this, Real_Time_Data_Retention::history, revision_, last_update_time_ns_, total_update_count_, cache_state_->size()};
}

template <class Value_Type, class Container, Mutex_Type Mutex, class Observer>
Real_Time_Data_Retention History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::retention() const noexcept {
    return Real_Time_Data_Retention::history;
}
template <class Value_Type, class Container, Mutex_Type Mutex, class Observer>
std::size_t History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::discard_before_time_ns(std::uint64_t time_ns) {
    std::lock_guard mutation_lock(mutation_mutex_);
    Real_Time_Data_Observation observation;
    std::size_t discarded{};
    {
        std::lock_guard<Mutex> lock(mutex_);
        while (discarded < update_times_size_ && update_times_[discarded] < time_ns) {
            ++discarded;
        }
        if (discarded == 0) {
            return 0;
        }
        cache_state_->erase(cache_state_->begin(), std::next(cache_state_->begin(), static_cast<std::ptrdiff_t>(discarded)));
        std::move(update_times_ + discarded, update_times_ + update_times_size_, update_times_);
        update_times_size_ -= discarded;
        ++revision_;
        observation = {Real_Time_Data_Observation_Event::discarded, {this, Real_Time_Data_Retention::history, revision_, last_update_time_ns_, total_update_count_, cache_state_->size()}};
    }
    observer_.observe(observation);
    return discarded;
}
template <class Value_Type, class Container, Mutex_Type Mutex, class Observer>
void History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::publish_render_state() {
    std::lock_guard<Mutex> lock(mutex_);
    if (render_revision_ == revision_)
        return;
    *scratch_state_ = *cache_state_;
    std::swap(render_state_, scratch_state_);
    render_revision_ = revision_;
}
template <class Value_Type, class Container, Mutex_Type Mutex, class Observer>
auto History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::render_state_value() const noexcept
    -> const Container& {
    return *render_state_;
}
