/*********************************************************************
 * \file   Reference.hpp
 *
 * \author Romain BOULLARD
 * \date   April 2026
 *********************************************************************/
#ifndef BIGFOOT_ENGINE_REFERENCE_HPP
#define BIGFOOT_ENGINE_REFERENCE_HPP
#include <Engine/Asset/AssetContainer.hpp>
#include <System/UUID/UUID.hpp>

#include <utility>

namespace Bigfoot
{
template<class ASSET>
class HardReference
{
  public:
    HardReference() = default;

    HardReference(const UUID& p_uuid):
        m_uuid(p_uuid)
    {
        Acquire();
    }

    HardReference(const HardReference& p_ref):
        m_uuid(p_ref.m_uuid)
    {
        Acquire();
    }

    HardReference(HardReference&& p_ref) noexcept:
        m_uuid(std::exchange(p_ref.m_uuid, UUID::NULL_UUID))
    {
    }

    [[nodiscard]]
    const UUID& GetUUID() const
    {
        return m_uuid;
    }

    ~HardReference()
    {
        Release();
    }

    HardReference& operator=(const HardReference& p_ref)
    {
        if (this != &p_ref)
        {
            Release();
            m_uuid = p_ref.m_uuid;
            Acquire();
        }
        return *this;
    }

    HardReference& operator=(HardReference&& p_ref) noexcept
    {
        if (this != &p_ref)
        {
            Release();
            m_uuid = std::exchange(p_ref.m_uuid, UUID::NULL_UUID);
        }
        return *this;
    }

    [[nodiscard]]
    bool operator==(const HardReference& p_ref) const
    {
        return m_uuid == p_ref.m_uuid;
    }

  private:
    void Acquire()
    {
        if (m_uuid)
        {
            AssetContainer<ASSET>::Get().IncrementHardRefCount(m_uuid);
        }
    }

    void Release()
    {
        if (m_uuid)
        {
            AssetContainer<ASSET>::Get().DecrementHardRefCount(m_uuid);
        }
    }

    UUID m_uuid = UUID::NULL_UUID;
};

template<class ASSET>
class SoftReference
{
  public:
    SoftReference() = default;

    SoftReference(const UUID& p_uuid):
        m_uuid(p_uuid)
    {
        Acquire();
    }

    SoftReference(const SoftReference& p_ref):
        m_uuid(p_ref.m_uuid)
    {
        Acquire();
    }

    SoftReference(SoftReference&& p_ref) noexcept:
        m_uuid(std::exchange(p_ref.m_uuid, UUID::NULL_UUID))
    {
    }

    [[nodiscard]]
    const UUID& GetUUID() const
    {
        return m_uuid;
    }

    ~SoftReference()
    {
        Release();
    }

    SoftReference& operator=(const SoftReference& p_ref)
    {
        if (this != &p_ref)
        {
            Release();
            m_uuid = p_ref.m_uuid;
            Acquire();
        }
        return *this;
    }

    SoftReference& operator=(SoftReference&& p_ref) noexcept
    {
        if (this != &p_ref)
        {
            Release();
            m_uuid = std::exchange(p_ref.m_uuid, UUID::NULL_UUID);
        }
        return *this;
    }

    [[nodiscard]]
    bool operator==(const SoftReference& p_ref) const
    {
        return m_uuid == p_ref.m_uuid;
    }

  private:
    void Acquire()
    {
        if (m_uuid)
        {
            AssetContainer<ASSET>::Get().IncrementSoftRefCount(m_uuid);
        }
    }

    void Release()
    {
        if (m_uuid)
        {
            AssetContainer<ASSET>::Get().DecrementSoftRefCount(m_uuid);
        }
    }

    UUID m_uuid = UUID::NULL_UUID;
};
}   // namespace Bigfoot
#endif
