#pragma once
#include <algorithm>
#include <concepts>
#include <cstddef>
#include <memory>
#include <memory_resource>
#include <utility>
#include "renderive/base/observer/Observer.hpp"
#include "renderive/frame_control/Frame_Control.hpp"
#include "renderive/renderable/color/Concepts.hpp"
#include "renderive/state/Double_State_Strategy.hpp"
#include "base/Scene_Base.hpp"
#include "base/Frame_Viewport.hpp"
struct Scene2D_Frame_Data : Abstract_Frame {};
struct Scene2D_State {
    Frame_Viewport viewport;
};
template <class State>
concept Scene2D_State_Value = State_Value<State> && requires(State& state) {
    requires std::same_as<std::remove_cvref_t<decltype(state.viewport)>, Frame_Viewport>;
};
template <class Frame_Control, class... Args>
concept Scene2D_Frame_Control_Constructible = std::constructible_from<Frame_Control, Args...> || std::constructible_from<Frame_Control, std::pmr::memory_resource&, Args...>;
template <class Strategy = Low_Latency_Strategy<Scene2D_Frame_Data>, Color_Cache_Type Cache = Recording_Color_Cache, class State = Scene2D_State, class State_Observer = Observer_State<>, class Scene_Observer = Observer_State<>>
requires Frame_Control_Strategy_For<Strategy, Scene2D_Frame_Data> && Scene2D_State_Value<State>
class Scene2D_Context : public Double_State_Strategy<Scene_2D_Base, State, Atomic_Spin_Mutex, State_Observer>, public Scene_Compositor {
public:
    using Scene_State_Strategy = Double_State_Strategy<Scene_2D_Base, State, Atomic_Spin_Mutex, State_Observer>;
    using Frame_Control = Strategy;
    using Frame = typename Frame_Control::Frame;
    using Painter_Lease = typename Frame_Control::Painter_Lease;
    using Render_Lease = typename Frame_Control::Render_Lease;
    template <class... Args>
    requires Scene2D_Frame_Control_Constructible<Frame_Control, Args&&...>
    explicit Scene2D_Context(Args&&... args) : Scene2D_Context(*std::pmr::get_default_resource(), std::forward<Args>(args)...) {}
    template <class... Args>
    requires Scene2D_Frame_Control_Constructible<Frame_Control, Args&&...>
    explicit Scene2D_Context(std::pmr::memory_resource& memory_resource, Args&&... args)
        : Scene_State_Strategy(State{}, memory_resource), frame_control(make_frame_control(this->memory_resource(), std::forward<Args>(args)...)), final_color_cache_(make_cache(this->memory_resource())) {}
    template <class... Args>
    requires Scene2D_Frame_Control_Constructible<Frame_Control, Args&&...>
    Scene2D_Context(With_Observer<State_Observer> state_observer, With_Observer<Scene_Observer> scene_observer, Args&&... args)
        : Scene2D_Context(*std::pmr::get_default_resource(), std::move(state_observer), std::move(scene_observer), std::forward<Args>(args)...) {}
    template <class... Args>
    requires Scene2D_Frame_Control_Constructible<Frame_Control, Args&&...>
    Scene2D_Context(std::pmr::memory_resource& memory_resource, With_Observer<State_Observer> state_observer, With_Observer<Scene_Observer> scene_observer, Args&&... args)
        : Scene_State_Strategy(State{}, std::move(state_observer), memory_resource), frame_control(make_frame_control(this->memory_resource(), std::forward<Args>(args)...)), final_color_cache_(make_cache(this->memory_resource())), scene_observer_(std::move(scene_observer.observer)) {}
    ~Scene2D_Context() override {
        this->shutdown();
    }
    Frame_Control frame_control;
    template <class Function>
    void with_final_color_cache(Function&& function) {
        if (this->is_render_worker_thread()) {
            function(static_cast<const Cache&>(final_color_cache_));
            return;
        }
        auto lock = this->lock_render_idle();
        function(static_cast<const Cache&>(final_color_cache_));
    }
protected:
    Frame_Control_Strategy_Base& frame_control_strategy_impl() noexcept override {
        return frame_control;
    }
    const Frame_Control_Strategy_Base& frame_control_strategy_impl() const noexcept override {
        return frame_control;
    }
    std::shared_ptr<Color_Cache> make_renderable_color_cache() override {
        return make_cache_pointer(this->memory_resource());
    }
    Frame_Viewport frame_viewport() const override {
        return this->Scene_State_Strategy::render_state_value().viewport;
    }
    void begin_composite(const Composite_Render_Context&) override {
        final_color_cache_.clear();
    }
    void composite(const Composite_Render_Context& context) override {
        if (context.renderable && context.renderable->visible && context.color_cache)
            final_color_cache_.composite(*context.color_cache);
    }
    std::uint64_t acquire_scene_state() override {
        return this->Scene_State_Strategy::state_revision();
    }
    void observe_scene(const Scene_Base::Observation& observation) noexcept override {
        scene_observer_.observe(observation);
    }
    std::uint64_t observer_now_ns() const noexcept override {
        return scene_observer_.now_ns();
    }
private:
    template <class... Args>
    static Frame_Control make_frame_control(std::pmr::memory_resource& memory_resource, Args&&... args) {
        if constexpr (std::constructible_from<Frame_Control, std::pmr::memory_resource&, Args&&...>) {
            return Frame_Control(memory_resource, std::forward<Args>(args)...);
        } else {
            return Frame_Control(std::forward<Args>(args)...);
        }
    }
    static Cache make_cache(std::pmr::memory_resource& memory_resource) {
        if constexpr (std::constructible_from<Cache, std::pmr::memory_resource&>) {
            return Cache(memory_resource);
        } else {
            return Cache();
        }
    }
    static std::shared_ptr<Cache> make_cache_pointer(std::pmr::memory_resource& memory_resource) {
        std::pmr::polymorphic_allocator<Cache> allocator(&memory_resource);
        if constexpr (std::constructible_from<Cache, std::pmr::memory_resource&>) {
            return std::allocate_shared<Cache>(allocator, memory_resource);
        } else {
            return std::allocate_shared<Cache>(allocator);
        }
    }
    Cache final_color_cache_;
    Scene_Observer scene_observer_;
};
