/*
 * 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; }

#endif
