/*********************************************************************
 * \file   TaggedType.h
 *
 * \author Romain BOULLARD
 * \date   December 2025
 *********************************************************************/
#ifndef BIGFOOT_UTILS_TAGGEDTYPE_HPP
#define BIGFOOT_UTILS_TAGGEDTYPE_HPP

#include <compare>

namespace Bigfoot
{
template<typename TYPE>
class TaggedType
{
  public:
    constexpr TaggedType() = default;

    constexpr explicit TaggedType(TYPE p_value):
        m_value(p_value)
    {
    }

    constexpr TaggedType(const TaggedType& p_taggedType) = default;
    constexpr TaggedType(TaggedType&& p_taggedType) noexcept = default;

    ~TaggedType() = default;

    constexpr TaggedType& operator=(const TaggedType& p_taggedType) = default;
    constexpr TaggedType& operator=(TaggedType&& p_taggedType) noexcept = default;

    constexpr TaggedType& operator=(const TYPE& p_value)
    {
        m_value = p_value;
        return *this;
    }

    constexpr explicit operator TYPE() const
    {
        return m_value;
    }

    constexpr TYPE Get() const
    {
        return m_value;
    }

    constexpr auto operator<=>(const TaggedType& p_other) const = default;

    constexpr TaggedType& operator+=(const TYPE& p_value)
    {
        m_value += p_value;
        return *this;
    }

    constexpr TaggedType& operator-=(const TYPE& p_value)
    {
        m_value -= p_value;
        return *this;
    }

    constexpr TaggedType& operator*=(const TYPE& p_value)
    {
        m_value *= p_value;
        return *this;
    }

    constexpr TaggedType& operator/=(const TYPE& p_value)
    {
        m_value /= p_value;
        return *this;
    }

    constexpr TaggedType& operator+=(const TaggedType& p_other)
    {
        m_value += p_other.m_value;
        return *this;
    }

    constexpr TaggedType& operator-=(const TaggedType& p_other)
    {
        m_value -= p_other.m_value;
        return *this;
    }

    constexpr TaggedType& operator*=(const TaggedType& p_other)
    {
        m_value *= p_other.m_value;
        return *this;
    }

    constexpr TaggedType& operator/=(const TaggedType& p_other)
    {
        m_value /= p_other.m_value;
        return *this;
    }

    template<typename L, typename R>
    friend constexpr TaggedType operator+(L p_left, R p_right)
    {
        return p_left += p_right;
    }

    template<typename L, typename R>
    friend constexpr TaggedType operator-(L p_left, R p_right)
    {
        return p_left -= p_right;
    }

    template<typename L, typename R>
    friend constexpr TaggedType operator*(L p_left, R p_right)
    {
        return p_left *= p_right;
    }

    template<typename L, typename R>
    friend constexpr TaggedType operator/(L p_left, R p_right)
    {
        return p_left /= p_right;
    }

  private:
    /**
     * The value
     */
    TYPE m_value {};
};
}   // namespace Bigfoot

#endif
