/*========================== begin_copyright_notice ============================

Copyright (C) 2020-2021 Intel Corporation

SPDX-License-Identifier: MIT

============================= end_copyright_notice ===========================*/

#ifndef _ATTRIBUTES_H_
#define _ATTRIBUTES_H_

#include <unordered_map>

namespace vISA
{
    class IR_Builder;
    class Attributes
    {
    public:
        /// <summary>
        /// Attribute kind, allow an attribute to be of multiple kinds.
        /// </summary>
        enum AttrKind
        {
            AK_KERNEL = 0x1,
            AK_VAR    = 0x2,
            AK_MASK   = 0x3
        };

        /// <summary>
        ///  Attribute value Types
        /// </summary>
        enum class AttrType : uint16_t
        {
            Bool, Int32, Int64, CString
        };

        struct SAttrVal {
            AttrType m_attrType;
            union {
                uint64_t     m_mem;  // WA to designated initialization
                int64_t      m_i64;
                int32_t      m_i32;
                const char*  m_cstr;
                bool         m_bool;
            } u;
        };

        struct SAttrInfo
        {
            // Attribute kind : one bit to denote one kind.
            // An attr can be of multiple kinds, for example, both kernel and var.
            uint16_t       m_attrKind;
            const char*    m_attrName;
            SAttrVal       m_defaultVal;     // type and default value
            const char*    m_description;
        };

        /// <summary>
        /// Attribute ID
        /// </summary>
        enum ID
        {
            #define DEF_ATTR(E, N, K, I, D)   E,
            #include "VISAAttributes.h"

            ATTR_TOTAL_NUM,
            ATTR_INVALID
        };

        // Used for current Attribute Value
        struct SAttrValue {
            SAttrVal m_val;
            bool     m_isSet;
        };

        /// Given an attribute name, return its ID
        static ID getAttributeID(const char* AttrName);

        static bool isValid(ID aID) { return (aID >= 0 && aID < ATTR_TOTAL_NUM); }

        /// Given an attribute ID, return its name
        static const char* getAttributeName(ID aID)
        {
            assert(isValid(aID));
            return AttrsInfo[(int)aID].m_attrName;
        }

        /// Return true if the given AttrName's ID == aID
        static bool isAttribute(ID aID, const char* AttrName)
        {
            return aID == getAttributeID(AttrName);
        }

        static bool isBool(ID aID)
        {
            assert(isValid(aID));
            return AttrsInfo[(int)aID].m_defaultVal.m_attrType == AttrType::Bool;
        }
        static bool isInt32(ID aID)
        {
            assert(isValid(aID));
            return AttrsInfo[(int)aID].m_defaultVal.m_attrType == AttrType::Int32;
        }
        static bool isInt64(ID aID)
        {
            assert(isValid(aID));
            return AttrsInfo[(int)aID].m_defaultVal.m_attrType == AttrType::Int64;
        }
        static bool isCStr(ID aID)
        {
            assert(isValid(aID));
            return AttrsInfo[(int)aID].m_defaultVal.m_attrType == AttrType::CString;
        }
        static bool isInt(ID aID) { return isInt32(aID) || isInt64(aID); }
        static bool isKernelAttr(ID aID)
        {
            assert(isValid(aID));
            return (AttrsInfo[(int)aID].m_attrKind & AK_KERNEL) != 0;
        }
        static bool isVarAttr(ID aID)
        {
            assert(isValid(aID));
            return (AttrsInfo[(int)aID].m_attrKind & AK_VAR) != 0;
        }
        static bool isIntKernelAttr(ID aID) { return isKernelAttr(aID) && isInt(aID); }
        static bool isStringKernelAttr(ID aID) { return isKernelAttr(aID) && isCStr(aID); }
        static bool isIntVarAttr(ID aID) { return isVarAttr(aID) && isInt(aID); }
        static bool isStringVarAttr(ID aID) { return isVarAttr(aID) && isCStr(aID); }

        // default attribute value
        static int32_t getInt32AttrDefault(ID aID)
        {
            assert(isValid(aID));
            return AttrsInfo[(int)aID].m_defaultVal.u.m_i32;
        }

        Attributes();
        ~Attributes() {};

        // Set attribute's value
        void setKernelAttr(ID kID, bool v);
        void setKernelAttr(ID kID, int32_t v, const IR_Builder& irb);
        void setKernelAttr(ID kID, int64_t v);
        void setKernelAttr(ID kID, const char* v);

        // Get Attribute's value
        bool getBoolKernelAttr(ID kID) const
        {
            SAttrValue* pAV = getKernelAttrValue(kID);
            assert(pAV->m_val.m_attrType == AttrType::Bool);
            return pAV->m_val.u.m_bool;
        }
        int32_t getInt32KernelAttr(ID kID) const
        {
            SAttrValue* pAV = getKernelAttrValue(kID);
            assert(pAV->m_val.m_attrType == AttrType::Int32);
            return pAV->m_val.u.m_i32;
        }
        int64_t getInt64KernelAttr(ID kID) const
        {
            SAttrValue* pAV = getKernelAttrValue(kID);
            assert(pAV->m_val.m_attrType == AttrType::Int64);
            return pAV->m_val.u.m_i64;
        }
        const char* getCStrKernelAttr(ID kID) const
        {
            SAttrValue* pAV = getKernelAttrValue(kID);
            assert(pAV->m_val.m_attrType == AttrType::CString);
            return pAV->m_val.u.m_cstr;
        }
        bool isKernelAttrSet(ID kID) const
        {
            SAttrValue* pAV = getKernelAttrValue(kID);
            return pAV->m_isSet;
        }

    private:
        /// <summary>
        /// Attribute meta info
        /// </summary>
        static SAttrInfo AttrsInfo[ATTR_TOTAL_NUM];

        SAttrValue* getKernelAttrValue(ID kID) const
        {
            auto II = m_kernelAttrs.find(kID);
            assert(II != m_kernelAttrs.end());
            SAttrValue* pAV = II->second;
            return pAV;
        }

        // Storage for holding per-kernel attr.
        SAttrValue m_attrValueStorage[ATTR_TOTAL_NUM];
        std::unordered_map<int, SAttrValue*> m_kernelAttrs;
    };
};
#endif // _ATTRIBUTES_H_
