ReflectedEnum.h

// ReflectedEnum.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2022

@import "IObject.h"
@import "ReflectedType.h"

namespace ceda
{
/*
Example:

    $enum+ EColour
    {
        red = 10,
        -green = 20,
        default blue = 30 : ["sky"]
    };

    GetCount() = 3
    
    GetValue(0) = 10
    GetValue(1) = 20
    GetValue(2) = 30
    
    GetString(0) = "red"
    GetString(1) = "green"
    GetString(2) = "blue"
    
    GetEnable(0) = true    
    GetEnable(1) = false
    GetEnable(2) = true    
*/

$struct+ ReflectedEnumTag <<os>>
{
    $ConstStringZ GetName() const { return name; }
    void Write(xostream& os) const;
    
    //////////////// State //////////////////

    ConstStringZ name;
    const octet_t* metaData;
    int32 value;
    bool enable;
};

$enum+ class EReflectedEnumFlagBits
{
    AsClass,              // If bit is set then it's an enum class  
    ValuesRedefined,      // If bit is unset then for all i tags[i].value == i
    ValuesDisabled        // If bit is unset then for all i tags[i].enable == true  
};

$struct+ ReflectedEnum <<os>>
{
    bool operator<(const ReflectedEnum& rhs) const { return this < &rhs; }
    bool operator==(const ReflectedEnum& rhs) const { return this == &rhs; }

    $ConstStringZ GetName() const { return name; }

    $bool IsEnumClass() const { return (flags & (1 << EReflectedEnumFlagBits::AsClass)) != 0; }

    // Returns true if the enum values have been redefined (i.e. they are not simply 0,1,2...)
    $bool ValuesRedefined() const { return (flags & (1 << EReflectedEnumFlagBits::ValuesRedefined)) != 0; }
    
    // Returns true if any of the enum tags have been disabled
    $bool ValuesDisabled() const { return (flags & (1 << EReflectedEnumFlagBits::ValuesDisabled)) != 0; }

    // Returns the number of distinct enum values
    $int32 GetCount() const { return count; }
    
    // Returns the ith enum value, which is just i if the enum values are simply 0,1,2,...
    $int32 GetValue(int32 i) const { return tags[i].value; }

    // Returns true if the ith enum value is enabled (i.e. no deprecated)
    $bool GetEnable(int32 i) const { return tags[i].enable; }
    
    // Returns the ith enum string
    $ConstStringZ GetString(int32 i) const { return tags[i].name; }

    // This checks whether the tag value exists, regardless of whether it has been disabled
    $bool IsValidValue(int32 v) const
    {
        if (ValuesRedefined())
        {
            for (int i=0 ; i < count ; ++i)
            {
                if (tags[i].value == v) return true;
            }
            return false;
        }
        else
        {
            return 0 <= v && v < count;
        }
    }

    void Write(xostream& os) const;

    //////////////// State //////////////////

    uint32 flags;
    @if (!CEDA_ENABLE_STRING_TABLES)
    {
        const octet_t* byteCode;
    }
    ConstStringZ name;
    const octet_t* metaData;
    ConstStringZ const* stringTable;
    int32 count;
    const ReflectedEnumTag* tags;
    int32 defaultIndex;
};

template<typename T>
struct GetReflectedType_class<T, std::enable_if_t<!std::is_const<T>::value && !std::is_volatile<T>::value && IsReflectedEnum<T>::value>>
{
    static inline ReflectedType get()
    {
        ReflectedType r;
        r.tag = EReflectedTypeTag::Enum;
        r.Enum = &GetReflectedEnum<T>();
        return r;
    }
};
} // namespace ceda