#pragma once
#include <cstdint>
#include <mutex>
#include <type_traits>
#include <utility>
#include "renderive/base/Concepts.hpp"
#include "Disabled_Observer.hpp"
#include "concept/Observer.hpp"
#include "concept/Time_Source.hpp"
#include "time_source/Steady_Time_Source.hpp"
template <class Observer = Disabled_Observer, Observer_Time_Source Time_Source = Steady_Time_Source, Mutex_Type Mutex = std::recursive_mutex>
class Observer_State {
public:
    static constexpr bool enabled = Observer::enabled;
    Observer_State() = default;
    explicit Observer_State(Observer observer) : observer_(std::move(observer)) {}
    Observer_State(Observer observer, Time_Source time_source) : observer_(std::move(observer)), time_source_(std::move(time_source)) {}
    Observer_State(const Observer_State&) = delete;
    Observer_State& operator=(const Observer_State&) = delete;
    Observer_State(Observer_State&& other) noexcept(std::is_nothrow_move_constructible_v<Observer> && std::is_nothrow_move_constructible_v<Time_Source> && std::is_nothrow_default_constructible_v<Mutex>) requires std::move_constructible<Observer> && std::move_constructible<Time_Source>
        : observer_(std::move(other.observer_)), time_source_(std::move(other.time_source_)) {}
    Observer_State& operator=(Observer_State&&) = delete;
    std::uint64_t now_ns() const noexcept {
        std::lock_guard<Mutex> lock(time_source_mutex_);
        return static_cast<std::uint64_t>(time_source_.now_ns());
    }
    template <class Target>
    requires Observer::enabled && requires(Observer& observer, Target& target) {
        observer.bind(target);
    }
    decltype(auto) bind(Target& target) {
        std::lock_guard<Mutex> lock(mutex_);
        return observer_.bind(target);
    }
    template <Observation_Struct Observation>
    requires Struct_Observer<Observer, Observation>
    void observe(const Observation& observation) noexcept {
        if constexpr (Observer::enabled) {
            std::lock_guard<Mutex> lock(mutex_);
            observer_.observe(observation);
        }
    }
private:
    struct Disabled_Mutex {};
    [[no_unique_address]] Observer observer_;
    [[no_unique_address]] Time_Source time_source_;
    mutable Mutex time_source_mutex_;
    [[no_unique_address]] std::conditional_t<Observer::enabled, Mutex, Disabled_Mutex> mutex_;
};
template <class Observer>
struct With_Observer {
    Observer observer;
};
template <class Observer>
With_Observer(Observer) -> With_Observer<Observer>;
