#pragma once
#include <concepts>
#include <functional>
#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 Double_State_Strategy : That, State_Strategy_Base {
    using Self = Double_State_Strategy;
    using State = State_Type;
    enum class Observation_Event {
        cache_updated,
        published
    };
    struct Observation {
        Observation_Event event{};
        std::uint64_t time_ns{};
        std::uint64_t cache_update_count{};
        std::uint64_t publish_count{};
        State state;
    };
    static_assert(Timed_Struct_Observer<Observer, Observation>);
    Double_State_Strategy() requires std::default_initializable<That> && std::default_initializable<State> : That(), states{}, render_state(&states[0]), cache_state(&states[1]), scratch_state(&states[2]) {}
    explicit Double_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]), cache_state(&states[1]), scratch_state(&states[2]) {}
    template <class... Args>
    explicit Double_State_Strategy(const State& state, Args&&... args)
        : That(std::forward<Args>(args)...), states{state, state, state}, render_state(&states[0]), cache_state(&states[1]), scratch_state(&states[2]) {}
    template <class... Args>
    Double_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}, render_state(&states[0]), cache_state(&states[1]), scratch_state(&states[2]) {}
    template <auto Member, Property_Member_Assignable<State, Member> Value>
    Self& set(Value&& value) {
        std::optional<Observation> observation;
        {
            std::lock_guard lock(mtx);
            cache_state->*Member = std::forward<Value>(value);
            ++cache_update_count;
            observation.emplace(Observation_Event::cache_updated, observer.now_ns(), cache_update_count, publish_count, *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;
        }
    }
    template <class Read>
    requires std::invocable<Read, const State&>
    auto read(Read&& value) const {
        std::lock_guard lock(mtx);
        return std::invoke(std::forward<Read>(value), *cache_state);
    }
    template <class Update>
    requires std::invocable<Update, State&>
    Self& update(Update&& value) {
        std::optional<Observation> observation;
        {
            std::lock_guard lock(mtx);
            std::invoke(std::forward<Update>(value), *cache_state);
            ++cache_update_count;
            observation.emplace(Observation_Event::cache_updated, observer.now_ns(), cache_update_count, publish_count, *cache_state);
        }
        observer.observe(*observation);
        return *this;
    }
    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, *cache_state);
            std::swap(render_state, scratch_state);
            ++publish_count;
        }
        observer.observe(*observation);
    }
    std::uint64_t state_revision() const override {
        std::lock_guard lock(mtx);
        return publish_count;
    }
protected:
    const State& render_state_value() const noexcept {
        return *render_state;
    }
private:
    friend class Render_State_View;
    Observer observer;
    State states[3];
    State* render_state;
    State* cache_state;
    State* scratch_state;
    std::uint64_t cache_update_count{};
    std::uint64_t publish_count{};
    mutable Mutex mtx;
};
