#pragma once

#include <utility>

#include "adapters/hooks/HookLifecycle.h"
#include "adapters/windows/rendering/Dx11Backend.h"

namespace ta::bootstrap
{
    class PresentHookHandle final
    {
    public:
        PresentHookHandle() noexcept = default;
        ~PresentHookHandle() noexcept { reset_best_effort(); }

        PresentHookHandle(const PresentHookHandle&) = delete;
        PresentHookHandle& operator=(const PresentHookHandle&) = delete;

        PresentHookHandle(PresentHookHandle&& other) noexcept
            : active_(std::exchange(other.active_, false)),
              detached_(std::exchange(other.detached_, false)) {}

        PresentHookHandle& operator=(PresentHookHandle&& other) noexcept
        {
            if (this != &other)
            {
                reset_best_effort();
                active_ = std::exchange(other.active_, false);
                detached_ = std::exchange(other.detached_, false);
            }
            return *this;
        }

        [[nodiscard]] static PresentHookHandle adopt() noexcept
        {
            PresentHookHandle handle;
            handle.active_ = true;
            return handle;
        }

        [[nodiscard]] bool detach() noexcept
        {
            if (!active_ || detached_)
                return true;
            detached_ = ta::adapters::windows::rendering::dx11_backend::detach();
            return detached_;
        }

        void release_resources() noexcept
        {
            if (!active_ || !detached_)
                return;
            ta::adapters::windows::rendering::dx11_backend::shutdown_resources();
            active_ = false;
            detached_ = false;
        }

        [[nodiscard]] bool active() const noexcept { return active_; }

    private:
        void reset_best_effort() noexcept
        {
            if (active_ && detach())
                release_resources();
        }

        bool active_ = false;
        bool detached_ = false;
    };

    class FeatureHooksHandle final
    {
    public:
        FeatureHooksHandle() noexcept = default;
        ~FeatureHooksHandle() noexcept { reset_best_effort(); }

        FeatureHooksHandle(const FeatureHooksHandle&) = delete;
        FeatureHooksHandle& operator=(const FeatureHooksHandle&) = delete;

        FeatureHooksHandle(FeatureHooksHandle&& other) noexcept
            : active_(std::exchange(other.active_, false)),
              detached_(std::exchange(other.detached_, false)) {}

        FeatureHooksHandle& operator=(FeatureHooksHandle&& other) noexcept
        {
            if (this != &other)
            {
                reset_best_effort();
                active_ = std::exchange(other.active_, false);
                detached_ = std::exchange(other.detached_, false);
            }
            return *this;
        }

        [[nodiscard]] static FeatureHooksHandle adopt() noexcept
        {
            FeatureHooksHandle handle;
            handle.active_ = true;
            return handle;
        }

        [[nodiscard]] bool detach() noexcept
        {
            if (!active_ || detached_)
                return true;
            detached_ = ta::adapters::hooks::lifecycle::detach_all();
            return detached_;
        }

        void release_state() noexcept
        {
            if (!active_ || !detached_)
                return;
            ta::adapters::hooks::lifecycle::reset_all();
            active_ = false;
            detached_ = false;
        }

        [[nodiscard]] bool active() const noexcept { return active_; }

    private:
        void reset_best_effort() noexcept
        {
            if (active_ && detach())
                release_state();
        }

        bool active_ = false;
        bool detached_ = false;
    };
}
