#pragma once
#include <condition_variable>
#include <cstddef>
#include <mutex>
#include <utility>
class Scene_Base;
class Scene_Lifetime {
public:
    class Lease {
    public:
        Lease() = default;
        Lease(const Lease&) = delete;
        Lease& operator=(const Lease&) = delete;
        Lease(Lease&& other) noexcept : owner_(std::exchange(other.owner_, nullptr)), scene_(std::exchange(other.scene_, nullptr)) {}
        Lease& operator=(Lease&& other) noexcept {
            if (this == &other) {
                return *this;
            }
            release();
            owner_ = std::exchange(other.owner_, nullptr);
            scene_ = std::exchange(other.scene_, nullptr);
            return *this;
        }
        ~Lease() {
            release();
        }
        explicit operator bool() const noexcept {
            return scene_ != nullptr;
        }
        Scene_Base& scene() const noexcept {
            return *scene_;
        }
    private:
        friend class Scene_Lifetime;
        Lease(Scene_Lifetime* owner, Scene_Base* scene) noexcept : owner_(owner), scene_(scene) {}
        void release() noexcept {
            if (!owner_) {
                return;
            }
            owner_->release();
            owner_ = nullptr;
            scene_ = nullptr;
        }
        Scene_Lifetime* owner_{};
        Scene_Base* scene_{};
    };
    explicit Scene_Lifetime(Scene_Base& scene) noexcept : scene_(&scene) {}
    Lease acquire() noexcept {
        std::lock_guard lock(mutex_);
        if (!scene_) {
            return {};
        }
        ++lease_count_;
        return Lease(this, scene_);
    }
    void invalidate() noexcept {
        std::unique_lock lock(mutex_);
        scene_ = nullptr;
        condition_.wait(lock, [this] {
            return lease_count_ == 0;
        });
    }
private:
    void release() noexcept {
        std::lock_guard lock(mutex_);
        --lease_count_;
        if (lease_count_ == 0) {
            condition_.notify_all();
        }
    }
    mutable std::mutex mutex_;
    std::condition_variable condition_;
    Scene_Base* scene_{};
    std::size_t lease_count_{};
};
