#pragma once
#include <concepts>
#include <mutex>
#include <optional>
#include <type_traits>
#include <utility>
#include "renderive/base/Atomic_Mutex.hpp"
#include "renderive/base/Concepts.hpp"
#include "renderive/base/observer/Observer.hpp"
#include "renderive/base/property/Concepts.hpp"
#include "Concepts.hpp"
#include "Render_State_View.hpp"
#include "base/State_Strategy_Base.hpp"
template <Derivable_Type That, State_Value State_Type, Mutex_Type Mutex = Atomic_Spin_Mutex, class Observer = Observer_State<>>
struct Triple_State_Strategy : That, State_Strategy_Base {
    using Self = Triple_State_Strategy;
    using State = State_Type;
    enum class Observation_Event {
        cache_updated,
        published,
        render_acquired
    };
    struct Observation {
        Observation_Event event{};
        std::uint64_t time_ns{};
        std::uint64_t cache_update_count{};
        std::uint64_t publish_count{};
        std::uint64_t render_revision{};
        State state;
    };
    static_assert(Timed_Struct_Observer<Observer, Observation>);
    Triple_State_Strategy() requires std::default_initializable<That> && std::default_initializable<State>
        : That(), states{}, render_state(&states[0]), published_state(&states[1]), cache_state(&states[2]), scratch_state(&states[3]) {}
    explicit Triple_State_Strategy(With_Observer<Observer> option) requires std::default_initializable<That> && std::default_initializable<State>
        : That(), observer(std::move(option.observer)), states{}, render_state(&states[0]), published_state(&states[1]), cache_state(&states[2]), scratch_state(&states[3]) {}
    template <class... Args>
    requires std::constructible_from<That, Args&&...>
    explicit Triple_State_Strategy(const State& state, Args&&... args)
        : That(std::forward<Args>(args)...), states{state, state, state, state}, render_state(&states[0]), published_state(&states[1]), cache_state(&states[2]), scratch_state(&states[3]) {}
    template <class... Args>
    requires std::constructible_from<That, Args&&...>
    Triple_State_Strategy(const State& state, With_Observer<Observer> option, Args&&... args)
        : That(std::forward<Args>(args)...), observer(std::move(option.observer)), states{state, state, state, state}, render_state(&states[0]), published_state(&states[1]), cache_state(&states[2]), scratch_state(&states[3]) {}
    template <auto Member, Property_Member_Assignable<State, Member> Value>
    Self& set(Value&& value) {
        Observation observation;
        {
            std::lock_guard lock(mtx);
            cache_state->*Member = std::forward<Value>(value);
            ++cache_update_count;
            observation = {Observation_Event::cache_updated, observer.now_ns(), cache_update_count, publish_count, render_revision, *cache_state};
        }
        observer.observe(observation);
        return *this;
    }
    template <auto Member>
    auto get() const {
        std::lock_guard lock(mtx);
        const auto& value = cache_state->*Member;
        if constexpr (Validated_Property_Value<std::remove_cvref_t<decltype(value)>>) {
            return value.get();
        } else {
            return value;
        }
    }
    void publish() override {
        std::optional<Observation> observation;
        {
            std::lock_guard lock(mtx);
            *scratch_state = *cache_state;
            observation.emplace(Observation_Event::published, observer.now_ns(), cache_update_count, publish_count + 1, render_revision, *cache_state);
            std::swap(published_state, scratch_state);
            ++publish_count;
            published_revision = publish_count;
        }
        observer.observe(*observation);
    }
    std::uint64_t acquire_render_state() {
        std::optional<Observation> observation;
        std::uint64_t revision{};
        {
            std::lock_guard lock(mtx);
            if (render_revision != published_revision) {
                observation.emplace(Observation_Event::render_acquired, observer.now_ns(), cache_update_count, publish_count, published_revision, *published_state);
                std::swap(render_state, published_state);
                render_revision = published_revision;
            }
            revision = render_revision;
        }
        if (observation) {
            observer.observe(*observation);
        }
        return revision;
    }
    std::uint64_t state_revision() const override {
        std::lock_guard lock(mtx);
        return publish_count;
    }
private:
    friend class Render_State_View;
    const State& render_state_value() const noexcept {
        return *render_state;
    }
    Observer observer;
    State states[4];
    State* render_state;
    State* published_state;
    State* cache_state;
    State* scratch_state;
    std::uint64_t cache_update_count{};
    std::uint64_t publish_count{};
    std::uint64_t published_revision{};
    std::uint64_t render_revision{};
    mutable Mutex mtx;
};
