#ifndef FLAGS_HPP_
#define FLAGS_HPP_

#include <Serialize/Serialize.hpp>

/**
 * @brief Represents a set of flags, represented by an integer type.
 * 
 * @tparam TFlag Flag type, should be an enum.
 * @tparam TRepr The type which the flags are represented as.
 */
template<typename TFlag, typename TRepr = uint32_t>
class Flags
{
private:
    TRepr m_value;

    TRepr getMask(TFlag flag) const
    {
        return (TRepr) 1 << static_cast<int>(flag);
    }

    typedef Flags<TFlag, TRepr> TSelf;

public:
    Flags() : m_value(0) {}
    Flags(const TSelf &other) : m_value(other.m_value) {}
    Flags(TSelf &&other) : m_value(other.m_value) {}

    /**
     * @brief Check if the specified flag is contained in the set.
     * 
     * @param flag The flag.
     */
    bool contains(TFlag flag) const
    {
        return (m_value & getMask(flag)) != 0;
    }

    TSelf& operator=(TSelf other)
    {
        m_value = other.m_value;
        return *this;
    }

    TSelf& operator=(TRepr repr)
    {
        m_value = repr;
        return *this;
    }

    TSelf& operator+=(TFlag flag)
    {
        m_value |= getMask(flag);
        return *this;
    }

    TSelf operator+(TFlag flag)
    {
        TSelf result = *this;
        result += flag;
        return result;
    }

    TSelf& operator-=(TFlag flag)
    {
        m_value &= ~getMask(flag);
        return *this;
    }

    TSelf operator-(TFlag flag)
    {
        TSelf result = *this;
        result -= flag;
        return result;
    }

    TSelf& operator|=(TSelf other)
    {
        m_value |= other.m_value;
        return *this;
    }

    TRepr unpack() const
    {
        return m_value;
    }

    /**
     * @brief Set all bits.
     */
    void setAll()
    {
        m_value = ~((TRepr)0);
    }
};

template<typename TFlag, typename TRepr>
struct SerialCodec<Flags<TFlag, TRepr>> : public PlainSerialCodec<Flags<TFlag, TRepr>> {};

#endif