/*
 * Copyright 2023 Rive
 */

#ifndef _RIVE_ENUM_BITSET_HPP_
#define _RIVE_ENUM_BITSET_HPP_

#include <type_traits>

namespace rive
{
// Wraps an enum containing a bitfield so that we can do implicit bool
// conversions.
template <typename T> class EnumBitset
{
public:
    using UnderlyingType = typename std::underlying_type<T>::type;
    constexpr EnumBitset() = default;
    constexpr EnumBitset(T value) : m_bits(static_cast<UnderlyingType>(value))
    {}
    constexpr UnderlyingType bits() const { return m_bits; }
    constexpr T value() const { return static_cast<T>(m_bits); }
    constexpr operator T() const { return value(); }
    constexpr operator bool() const { return m_bits != 0; }

private:
    UnderlyingType m_bits = 0;
};
} // namespace rive

#define RIVE_MAKE_ENUM_BITSET(ENUM)                                            \
    constexpr inline ::rive::EnumBitset<ENUM> operator&(                       \
        ::rive::EnumBitset<ENUM> lhs,                                          \
        ::rive::EnumBitset<ENUM> rhs)                                          \
    {                                                                          \
        return static_cast<ENUM>(lhs.bits() & rhs.bits());                     \
    }                                                                          \
    constexpr inline ::rive::EnumBitset<ENUM> operator&(                       \
        ENUM lhs,                                                              \
        ::rive::EnumBitset<ENUM> rhs)                                          \
    {                                                                          \
        return ::rive::EnumBitset<ENUM>(lhs) & rhs;                            \
    }                                                                          \
    constexpr inline ::rive::EnumBitset<ENUM> operator&(                       \
        ::rive::EnumBitset<ENUM> lhs,                                          \
        ENUM rhs)                                                              \
    {                                                                          \
        return lhs & ::rive::EnumBitset<ENUM>(rhs);                            \
    }                                                                          \
    constexpr inline ::rive::EnumBitset<ENUM> operator&(ENUM lhs, ENUM rhs)    \
    {                                                                          \
        return ::rive::EnumBitset<ENUM>(lhs) & ::rive::EnumBitset<ENUM>(rhs);  \
    }                                                                          \
                                                                               \
    constexpr inline ::rive::EnumBitset<ENUM> operator|(                       \
        ::rive::EnumBitset<ENUM> lhs,                                          \
        ::rive::EnumBitset<ENUM> rhs)                                          \
    {                                                                          \
        return static_cast<ENUM>(lhs.bits() | rhs.bits());                     \
    }                                                                          \
    constexpr inline ::rive::EnumBitset<ENUM> operator|(                       \
        ENUM lhs,                                                              \
        ::rive::EnumBitset<ENUM> rhs)                                          \
    {                                                                          \
        return ::rive::EnumBitset<ENUM>(lhs) | rhs;                            \
    }                                                                          \
    constexpr inline ::rive::EnumBitset<ENUM> operator|(                       \
        ::rive::EnumBitset<ENUM> lhs,                                          \
        ENUM rhs)                                                              \
    {                                                                          \
        return lhs | ::rive::EnumBitset<ENUM>(rhs);                            \
    }                                                                          \
    constexpr inline ::rive::EnumBitset<ENUM> operator|(ENUM lhs, ENUM rhs)    \
    {                                                                          \
        return ::rive::EnumBitset<ENUM>(lhs) | ::rive::EnumBitset<ENUM>(rhs);  \
    }                                                                          \
                                                                               \
    constexpr inline ::rive::EnumBitset<ENUM> operator~(                       \
        ::rive::EnumBitset<ENUM> rhs)                                          \
    {                                                                          \
        return static_cast<ENUM>(~rhs.bits());                                 \
    }                                                                          \
    constexpr inline ::rive::EnumBitset<ENUM> operator~(ENUM rhs)              \
    {                                                                          \
        return ~::rive::EnumBitset<ENUM>(rhs);                                 \
    }                                                                          \
                                                                               \
    inline ENUM& operator&=(ENUM& lhs, ::rive::EnumBitset<ENUM> rhs)           \
    {                                                                          \
        return lhs = lhs & rhs;                                                \
    }                                                                          \
    inline ENUM& operator&=(ENUM& lhs, ENUM rhs) { return lhs = lhs & rhs; }   \
                                                                               \
    inline ENUM& operator|=(ENUM& lhs, ::rive::EnumBitset<ENUM> rhs)           \
    {                                                                          \
        return lhs = lhs | rhs;                                                \
    }                                                                          \
    inline ENUM& operator|=(ENUM& lhs, ENUM rhs) { return lhs = lhs | rhs; }

#define RIVE_DECL_ENUM_BITSET_FRIENDS(ENUM)                                    \
    friend constexpr ::rive::EnumBitset<ENUM> operator&(                       \
        ::rive::EnumBitset<ENUM>,                                              \
        ::rive::EnumBitset<ENUM>);                                             \
    friend constexpr ::rive::EnumBitset<ENUM> operator&(                       \
        ENUM,                                                                  \
        ::rive::EnumBitset<ENUM>);                                             \
    friend constexpr ::rive::EnumBitset<ENUM> operator&(                       \
        ::rive::EnumBitset<ENUM>,                                              \
        ENUM);                                                                 \
    friend constexpr ::rive::EnumBitset<ENUM> operator&(ENUM, ENUM);           \
    friend constexpr ::rive::EnumBitset<ENUM> operator|(                       \
        ::rive::EnumBitset<ENUM>,                                              \
        ::rive::EnumBitset<ENUM>);                                             \
    friend constexpr ::rive::EnumBitset<ENUM> operator|(                       \
        ENUM,                                                                  \
        ::rive::EnumBitset<ENUM>);                                             \
    friend constexpr ::rive::EnumBitset<ENUM> operator|(                       \
        ::rive::EnumBitset<ENUM>,                                              \
        ENUM);                                                                 \
    friend constexpr ::rive::EnumBitset<ENUM> operator|(ENUM lhs, ENUM rhs);   \
    friend constexpr ::rive::EnumBitset<ENUM> operator~(                       \
        ::rive::EnumBitset<ENUM> rhs);                                         \
    friend constexpr ::rive::EnumBitset<ENUM> operator~(ENUM rhs);             \
    friend ENUM& operator&=(ENUM& lhs, ::rive::EnumBitset<ENUM> rhs);          \
    friend ENUM& operator&=(ENUM& lhs, ENUM rhs);                              \
    friend ENUM& operator|=(ENUM& lhs, ::rive::EnumBitset<ENUM> rhs);          \
    friend ENUM& operator|=(ENUM& lhs, ENUM rhs);

#endif
