/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A.  All Rights Reserved.                         *
*                                                                          *
****************************************************************************/

#pragma once

#include "glmCoreAPI.h"
#include "glmMemory.h"
#include "glmUuid.h"
#include "glmDebugging.h"
#include <algorithm>
#include "glmMap.h"

namespace glm
{
    typedef Uuid TypeID;
    class AbstractAttribute;
    template <class TValue, class TOwner>
    class Attribute;
    template <class TValue, class TOwner>
    class AccessorsAttribute;
    template <class TValue, class TOwner>
    class ContainerAccessorsAttribute;
    extern size_t RTTIConcatBufferIndex;

    //-------------------------------------------------------------------------
    class IRTTICreator
    {
    public:
        GLM_ABSTRACTCLASS_STAT_ID( glm::MemoryStatIds::GolaemShared)

        virtual ~IRTTICreator()
        {
        }
        virtual void* create() const = 0;
    };

    //-------------------------------------------------------------------------
    template <class T>
    class RTTICreatorT : public IRTTICreator
    {
    public:
        GLM_CLASS_STAT_ID( glm::MemoryStatIds::GolaemShared)

        virtual ~RTTICreatorT()
        {
        }
        virtual void* create() const
        {
            return new T();
        }
    };

    //-------------------------------------------------------------------------
    // Run Time Type Identification description class.
    // Classes which support RTTI declare an instance of this class as a static member.
    // RTTI enables to identify the type ID of a class and its base classes.
    class GLM_CORE_API RTTI
    {
    public:
        GLM_CLASS_STAT_ID( glm::MemoryStatIds::GolaemShared)

        typedef void (*InformationFunc)();

        static RTTI* checkBeforeCreate(const TypeID& typeID, const char* name); // call checkBeforeCreate before create : _rtti of templates could to be NULL in the current module, while RTTI of the type is already registered by another module. checkBeforeCreate allow to get a single RTTI
        static RTTI* create(const TypeID& typeID, const char* name, IRTTICreator* creator = NULL);

        static void createRTTIs();
        static void destroyRTTIs();

        ~RTTI();
        RTTI& operator=(const RTTI& rtti); // does not copy the typeID and typeName

        const TypeID& getTypeID() const;
        const char* getTypeName() const;
        template <class T>
        bool isDerivedFrom() const;                 //return true if this RTTI is derived from the type specified by the typeID of T, as well as if typeID is the type ID of this class.
        bool isDerivedFrom(const RTTI& rtti) const; //return true if this RTTI is derived from the type specified by the typeID of rtti, as well as if typeID is the type ID of this class.
        unsigned int getBaseClassesCount() const;
        const RTTI& getBaseClassRTTI(unsigned int baseClass) const;
        template <class TBaseClass>
        void addBaseClass() const;

        unsigned int getAttributesCount() const;
        bool hasAttribute(const AbstractAttribute& attribute) const;
        const AbstractAttribute* getAttribute(unsigned int attributeNumber) const; // by convention, a class inherits from its base class attributes and attribute indices can be seen as sorted in increasing order from base classes to derived classes.
        template <class TAttribute>
        const TAttribute* getAttribute(unsigned int attributeNumber) const; // if you already know the actual type of the inspectable attribute, you can pass it as a template parameter to this method, which will make the cast for you.Note that passing the wrong type invalidates an assertion, which causes execution abortion in debug mode.
        const AbstractAttribute* getAttribute(const char* name) const;      // this method performs a breadth-first search for the attribute with the specified name, starting from the current RTTI, up through its base classes.
        template <class TAttribute>
        const TAttribute* getAttribute(const char* name) const;
        template <class TAttribute>
        void addAttribute(const TAttribute& attrib) const;
        inline void isRemoving(bool value)
        {
            _removingAttributes = value;
        }

        static const TypeID& getTypeID(const char* typeName);
        static const char* getTypeName(const TypeID& typeID);
        static const RTTI* getRTTI(const TypeID& typeID);
        static glm::PODArray<const RTTI*> getRTTIs(const TypeID& typeID); // get accessor to the RTTIs pointer derived from the typeID, including the one associated to typeID
        static void allocateStaticRTTIs();
        static void deallocateStaticRTTIs();
        static void* createInstance(const TypeID& typeID);

        inline unsigned int getCounter()
        {
            return _counter;
        }
        inline void incrementCounter()
        {
            ++_counter;
        }
        inline void decrementCounter()
        {
            --_counter;
        }

        typedef GlmMap<TypeID, IRTTICreator*> TypeIDToIRTTICreator;
        static TypeIDToIRTTICreator* _rttiFactory;

    private:
        static void printAlreadyRegisteredErrorMessage(const char* attributeName, const char* rttiTypeName);
        RTTI();
        RTTI(const RTTI& rtti);

        //HACK: defining typedef to allow use of memory allocation macros (doesn't support types name which contains ',' character)
        typedef GlmMap<glm::GlmStaticString, TypeID> TypeNameToTypeIDMAP;
        typedef GlmMap<TypeID, const RTTI*> TypeIDToRTTIMap;

        static TypeNameToTypeIDMAP* _typeNameToTypeID;
        static TypeNameToTypeIDMAP& getTypeNameToTypeID();
        static TypeIDToRTTIMap* _typeIDToRTTI;
        static TypeIDToRTTIMap& getTypeIDToRTTI();

        TypeID _typeID;
        const char* _typeName;
        uint32_t _counter; // counts usages of this RTTI. Beware that RTTI + creator + infoFunc/baseClass registration is called by first module creating it. Thus It is important to be symmetric in create/destroy calls
        mutable glm::PODArray<const RTTI*> _baseClassesRTTI;
        mutable glm::PODArray<AbstractAttribute*> _attributes;

        mutable bool _removingAttributes;
    };

    //-------------------------------------------------------------------------
    inline const TypeID& RTTI::getTypeID() const
    {
        return _typeID;
    }

    //-------------------------------------------------------------------------
    inline const char* RTTI::getTypeName() const
    {
        return _typeName;
    }

    //-------------------------------------------------------------------------
    inline unsigned int RTTI::getBaseClassesCount() const
    {
        return static_cast<unsigned int>(_baseClassesRTTI.size());
    }

    //-------------------------------------------------------------------------
    inline const RTTI& RTTI::getBaseClassRTTI(unsigned int baseClass) const
    {
        return *_baseClassesRTTI.at(baseClass);
    }

    //-------------------------------------------------------------------------
    inline const TypeID& getTypeID(const char* typeName)
    {
        return RTTI::getTypeID(typeName);
    }

    //-------------------------------------------------------------------------
    inline const char* getTypeName(const TypeID& id)
    {
        return RTTI::getTypeName(id);
    }

    //-------------------------------------------------------------------------
    template <class T>
    class HasVTable
    {
    public:
        class _Derived_ : public T
        {
            virtual ~_Derived_()
            {
            }
            virtual void _force_the_vtable()
            {
            }
        };
        enum
        {
            Value = (sizeof(T) == sizeof(_Derived_))
        };
    };
} // namespace glm

#include "glmRTTIAccessor.h"

//-------------------------------------------------------------------------
template <class T>
bool glm::RTTI::isDerivedFrom() const
{
    if (_typeID == RTTIAccessor<T>::getClassRTTI().getTypeID())
    {
        return true;
    }

    // perform a depth-first search for a base class with typeID.
    glm::PODArray<const RTTI*>::const_iterator ite(_baseClassesRTTI.begin());
    for (; ite != _baseClassesRTTI.end(); ++ite)
    {
        if ((*ite)->isDerivedFrom<T>())
        {
            return true;
        }
    }
    return false;
}

//-------------------------------------------------------------------------
template <class TBaseClass>
void glm::RTTI::addBaseClass() const
{
    if (!_removingAttributes)
    {
        // insure that init of base class is done, it is correct to init base class from "addBaseClass"
        RTTIAccessor<TBaseClass>::createClassRTTI();
        _baseClassesRTTI.push_back(&RTTIAccessor<TBaseClass>::getClassRTTI());
    }
    else
    {
        // insure that init of base class is done, it is correct to init base class from "addBaseClass"
        // _baseClassesRTTI array will be cleared when deleting RTTI, no need to find and process it here
        RTTIAccessor<TBaseClass>::destroyClassRTTI();
    }
}

namespace glm
{
    const char* RTTIConcat(const char* s1);
    GLM_CORE_API const char* RTTIConcat(const char* s1, const char* s2);
    GLM_CORE_API const char* RTTIConcat(const char* s1, const char* s2, const char* s3);
    GLM_CORE_API const char* RTTIConcat(const char* s1, const char* s2, const char* s3, const char* s4);
    GLM_CORE_API const char* RTTIConcat(const char* s1, const char* s2, const char* s3, const char* s4, const char* s5);
} // namespace glm

//-------------------------------------------------------------------------
#define GLM_RTTI_COMMON(typeID, type, name)                                                                                                                                                                             \
public:                                                                                                                                                                                                                 \
    static const glm::RTTI& getClassRTTI()                                                                                                                                                                              \
    {                                                                                                                                                                                                                   \
        if (_rtti == NULL)                                                                                                                                                                                              \
            _rtti = glm::RTTI::checkBeforeCreate(typeID, name);                                                                                                                                                         \
        GLM_DEBUG_ASSERT_MSG(_rtti != NULL, "%s should be explicitely initialized in module init, by calling %s::createClassRTTI(), before calling %s::getClassRTTI()", name, name, name);                              \
        return *_rtti;                                                                                                                                                                                                  \
    }                                                                                                                                                                                                                   \
    template <class TRTTIBaseClass>                                                                                                                                                                                     \
    static void addBaseClass()                                                                                                                                                                                          \
    {                                                                                                                                                                                                                   \
        type::getClassRTTI().addBaseClass<TRTTIBaseClass>();                                                                                                                                                            \
    }                                                                                                                                                                                                                   \
    template <class TRTTIAttribute>                                                                                                                                                                                     \
    static void addAttribute(const char* attributeName, TRTTIAttribute type::*attribute)                                                                                                                                \
    {                                                                                                                                                                                                                   \
        type::getClassRTTI().addAttribute(glm::Attribute<TRTTIAttribute, type>(attributeName, attribute));                                                                                                              \
    }                                                                                                                                                                                                                   \
    template <class TRTTIAttribute>                                                                                                                                                                                     \
    static void addAccessorsAttribute(const char* attributeName, TRTTIAttribute (type::*getter)() const, void (type::*setter)(TRTTIAttribute value))                                                                    \
    {                                                                                                                                                                                                                   \
        type::getClassRTTI().addAttribute(glm::AccessorsAttribute<TRTTIAttribute, type>(attributeName, getter, setter));                                                                                                \
    }                                                                                                                                                                                                                   \
    template <class TRTTIAttribute>                                                                                                                                                                                     \
    static void addContainerAccessorsAttribute(const char* attributeName, TRTTIAttribute (type::*getter)(unsigned int index) const, void (type::*setter)(unsigned int index, TRTTIAttribute value), unsigned int index) \
    {                                                                                                                                                                                                                   \
        type::getClassRTTI().addAttribute(glm::ContainerAccessorsAttribute<TRTTIAttribute, type>(attributeName, getter, setter, index));                                                                                \
    }                                                                                                                                                                                                                   \
                                                                                                                                                                                                                        \
private:                                                                                                                                                                                                                \
    static glm::RTTI* _rtti;

//-------------------------------------------------------------------------
#define GLM_RTTI_NAME(typeID, type, name, informationFunc)                                                                                                                                                                      \
    friend class glm::RTTICreatorT<type>;                                                                                                                                                                                       \
                                                                                                                                                                                                                                \
public:                                                                                                                                                                                                                         \
    static void createClassRTTI(bool createAttributes = true)                                                                                                                                                                   \
    {                                                                                                                                                                                                                           \
        GLM_COMPILE_TIME_ASSERT(!glm::HasVTable<type>::Value);                                                                                                                                                                  \
        if (_rtti == NULL)                                                                                                                                                                                                      \
        {                                                                                                                                                                                                                       \
            _rtti = glm::RTTI::checkBeforeCreate(typeID, name);                                                                                                                                                                 \
            if (_rtti == NULL)                                                                                                                                                                                                  \
            {                                                                                                                                                                                                                   \
                glm::IRTTICreator* rttiCreator = new glm::RTTICreatorT<type>();                                                                                                                                                 \
                _rtti = glm::RTTI::create(typeID, name, rttiCreator);                                                                                                                                                           \
            }                                                                                                                                                                                                                   \
        }                                                                                                                                                                                                                       \
        if (createAttributes)                                                                                                                                                                                                   \
        {                                                                                                                                                                                                                       \
            GLM_DEBUG_ASSERT_MSG(_rtti->getBaseClassesCount() == 0 && _rtti->getAttributesCount() == 0, "createClassRTTI() already called for %s, please remove this call and matching destroy if any.", _rtti->getTypeName()); \
            informationFunc();                                                                                                                                                                                                  \
        }                                                                                                                                                                                                                       \
        _rtti->incrementCounter();                                                                                                                                                                                              \
    }                                                                                                                                                                                                                           \
    static void destroyClassRTTI(bool deleteAttributes = true)                                                                                                                                                                  \
    {                                                                                                                                                                                                                           \
        if (_rtti != NULL)                                                                                                                                                                                                      \
        {                                                                                                                                                                                                                       \
            GLM_DEBUG_ASSERT(_rtti->getCounter() > 0);                                                                                                                                                                          \
            if (deleteAttributes)                                                                                                                                                                                               \
            {                                                                                                                                                                                                                   \
                _rtti->isRemoving(true);                                                                                                                                                                                        \
                informationFunc();                                                                                                                                                                                              \
                _rtti->isRemoving(false);                                                                                                                                                                                       \
            }                                                                                                                                                                                                                   \
            _rtti->decrementCounter();                                                                                                                                                                                          \
            if (_rtti->getCounter() == 0)                                                                                                                                                                                       \
            {                                                                                                                                                                                                                   \
                GLM_SAFE_DELETE(_rtti);                                                                                                                                                                                         \
            }                                                                                                                                                                                                                   \
        }                                                                                                                                                                                                                       \
    }                                                                                                                                                                                                                           \
    const glm::RTTI& getRTTI() const                                                                                                                                                                                            \
    {                                                                                                                                                                                                                           \
        return getClassRTTI();                                                                                                                                                                                                  \
    }                                                                                                                                                                                                                           \
    GLM_RTTI_COMMON(typeID, type, name)

#define GLM_ABSTRACT_RTTI_NAME(typeID, type, name, informationFunc)                                                                                                                                                             \
public:                                                                                                                                                                                                                         \
    static void createClassRTTI(bool createAttributes = true)                                                                                                                                                                   \
    {                                                                                                                                                                                                                           \
        if (_rtti == NULL)                                                                                                                                                                                                      \
        {                                                                                                                                                                                                                       \
            _rtti = glm::RTTI::checkBeforeCreate(typeID, name);                                                                                                                                                                 \
            if (_rtti == NULL)                                                                                                                                                                                                  \
            {                                                                                                                                                                                                                   \
                _rtti = glm::RTTI::create(typeID, name);                                                                                                                                                                        \
            }                                                                                                                                                                                                                   \
        }                                                                                                                                                                                                                       \
        if (createAttributes)                                                                                                                                                                                                   \
        {                                                                                                                                                                                                                       \
            GLM_DEBUG_ASSERT_MSG(_rtti->getBaseClassesCount() == 0 && _rtti->getAttributesCount() == 0, "createClassRTTI() already called for %s, please remove this call and matching destroy if any.", _rtti->getTypeName()); \
            informationFunc();                                                                                                                                                                                                  \
        }                                                                                                                                                                                                                       \
        _rtti->incrementCounter();                                                                                                                                                                                              \
    }                                                                                                                                                                                                                           \
    static void destroyClassRTTI(bool deleteAttributes = true)                                                                                                                                                                  \
    {                                                                                                                                                                                                                           \
        if (_rtti != NULL)                                                                                                                                                                                                      \
        {                                                                                                                                                                                                                       \
            if (deleteAttributes)                                                                                                                                                                                               \
            {                                                                                                                                                                                                                   \
                _rtti->isRemoving(true);                                                                                                                                                                                        \
                informationFunc();                                                                                                                                                                                              \
                _rtti->isRemoving(false);                                                                                                                                                                                       \
            }                                                                                                                                                                                                                   \
            GLM_DEBUG_ASSERT(_rtti->getCounter() > 0);                                                                                                                                                                          \
            _rtti->decrementCounter();                                                                                                                                                                                          \
            if (_rtti->getCounter() == 0)                                                                                                                                                                                       \
            {                                                                                                                                                                                                                   \
                delete _rtti;																																																	\
				_rtti = NULL;																																																	\
            }                                                                                                                                                                                                                   \
        }                                                                                                                                                                                                                       \
    }                                                                                                                                                                                                                           \
    virtual const glm::RTTI& getRTTI() const                                                                                                                                                                                    \
    {                                                                                                                                                                                                                           \
        return getClassRTTI();                                                                                                                                                                                                  \
    }                                                                                                                                                                                                                           \
    GLM_RTTI_COMMON(typeID, type, name)

#define GLM_VIRTUAL_RTTI_NAME(typeID, type, name, informationFunc)                                                                                                                                                              \
    friend class glm::RTTICreatorT<type>;                                                                                                                                                                                       \
                                                                                                                                                                                                                                \
public:                                                                                                                                                                                                                         \
    static void createClassRTTI(bool createAttributes = true)                                                                                                                                                                   \
    {                                                                                                                                                                                                                           \
        if (_rtti == NULL)                                                                                                                                                                                                      \
        {                                                                                                                                                                                                                       \
            _rtti = glm::RTTI::checkBeforeCreate(typeID, name);                                                                                                                                                                 \
            if (_rtti == NULL)                                                                                                                                                                                                  \
            {                                                                                                                                                                                                                   \
                glm::IRTTICreator* rttiCreator = new glm::RTTICreatorT<type>();                                                                                                                                                 \
                _rtti = glm::RTTI::create(typeID, name, rttiCreator);                                                                                                                                                           \
            }                                                                                                                                                                                                                   \
        }                                                                                                                                                                                                                       \
        if (createAttributes)                                                                                                                                                                                                   \
        {                                                                                                                                                                                                                       \
            GLM_DEBUG_ASSERT_MSG(_rtti->getBaseClassesCount() == 0 && _rtti->getAttributesCount() == 0, "createClassRTTI() already called for %s, please remove this call and matching destroy if any.", _rtti->getTypeName()); \
            informationFunc();                                                                                                                                                                                                  \
        }                                                                                                                                                                                                                       \
        _rtti->incrementCounter();                                                                                                                                                                                              \
    }                                                                                                                                                                                                                           \
    static void destroyClassRTTI(bool deleteAttributes = true)                                                                                                                                                                  \
    {                                                                                                                                                                                                                           \
        if (_rtti != NULL)                                                                                                                                                                                                      \
        {                                                                                                                                                                                                                       \
            if (deleteAttributes)                                                                                                                                                                                               \
            {                                                                                                                                                                                                                   \
                _rtti->isRemoving(true);                                                                                                                                                                                        \
                informationFunc();                                                                                                                                                                                              \
                _rtti->isRemoving(false);                                                                                                                                                                                       \
            }                                                                                                                                                                                                                   \
            GLM_DEBUG_ASSERT(_rtti->getCounter() > 0);                                                                                                                                                                          \
            _rtti->decrementCounter();                                                                                                                                                                                          \
            if (_rtti->getCounter() == 0)                                                                                                                                                                                       \
            {                                                                                                                                                                                                                   \
                GLM_SAFE_DELETE(_rtti);                                                                                                                                                                                         \
            }                                                                                                                                                                                                                   \
        }                                                                                                                                                                                                                       \
    }                                                                                                                                                                                                                           \
    virtual const glm::RTTI& getRTTI() const                                                                                                                                                                                    \
    {                                                                                                                                                                                                                           \
        return getClassRTTI();                                                                                                                                                                                                  \
    }                                                                                                                                                                                                                           \
    GLM_RTTI_COMMON(typeID, type, name)

//-------------------------------------------------------------------------
#define GLM_RTTI(typeID, type, informationFunc) GLM_RTTI_NAME(typeID, type, #type, informationFunc)

#define GLM_ABSTRACT_RTTI(typeID, type, informationFunc) GLM_ABSTRACT_RTTI_NAME(typeID, type, #type, informationFunc)

#define GLM_VIRTUAL_RTTI(typeID, type, informationFunc) GLM_VIRTUAL_RTTI_NAME(typeID, type, #type, informationFunc)

//-------------------------------------------------------------------------
#define GLM_RTTI_T(typeID, type, T, informationFunc) \
    typedef type<T> glm_class_type;                  \
    GLM_RTTI_NAME(glm::Uuid(typeID) + glm::getTypeID<T>(), glm_class_type, glm::RTTIConcat(#type, glm::getTypeName<T>()), informationFunc)

#define GLM_ABSTRACT_RTTI_T(typeID, type, T, informationFunc) \
    typedef type<T> glm_class_type;                           \
    GLM_ABSTRACT_RTTI_NAME(glm::Uuid(typeID) + glm::getTypeID<T>(), glm_class_type, glm::RTTIConcat(#type, glm::getTypeName<T>()), informationFunc)

#define GLM_VIRTUAL_RTTI_T(typeID, type, T, informationFunc) \
    typedef type<T> glm_class_type;                          \
    GLM_VIRTUAL_RTTI_NAME(glm::Uuid(typeID) + glm::getTypeID<T>(), glm_class_type, glm::RTTIConcat(#type, glm::getTypeName<T>()), informationFunc)

//-------------------------------------------------------------------------
#define GLM_RTTI_NT(typeID, type, NT, informationFunc) \
    typedef type<NT> glm_class_type;                   \
    GLM_RTTI_NAME(glm::Uuid(typeID) + NT, glm_class_type, glm::RTTIConcat(#type, glm::toString(NT).c_str()), informationFunc)

//-------------------------------------------------------------------------
#define GLM_RTTI_T0_T1(typeID, type, T0, T1, informationFunc) \
    typedef type<T0, T1> glm_class_type;                      \
    GLM_RTTI_NAME(glm::Uuid(typeID) + glm::getTypeID<T0>() + glm::getTypeID<T1>(), glm_class_type, glm::RTTIConcat(#type, glm::getTypeName<T0>(), glm::getTypeName<T1>()), informationFunc)

#define GLM_ABSTRACT_RTTI_T0_T1(typeID, type, T0, T1, informationFunc) \
    typedef type<T0, T1> glm_class_type;                               \
    GLM_ABSTRACT_RTTI_NAME(glm::Uuid(typeID) + glm::getTypeID<T0>() + glm::getTypeID<T1>(), glm_class_type, glm::RTTIConcat(#type, glm::getTypeName<T0>(), glm::getTypeName<T1>()), informationFunc)

#define GLM_VIRTUAL_RTTI_T0_T1(typeID, type, T0, T1, informationFunc) \
    typedef type<T0, T1> glm_class_type;                              \
    GLM_VIRTUAL_RTTI_NAME(glm::Uuid(typeID) + glm::getTypeID<T0>() + glm::getTypeID<T1>(), glm_class_type, glm::RTTIConcat(#type, glm::getTypeName<T0>(), glm::getTypeName<T1>()), informationFunc)

//-------------------------------------------------------------------------
#define GLM_RTTI_T0_T1_T2(typeID, type, T0, T1, T2, informationFunc) \
    typedef type<T0, T1, T2> glm_class_type;                         \
    GLM_RTTI_NAME(glm::Uuid(typeID) + glm::getTypeID<T0>() + glm::getTypeID<T1>() + glm::getTypeID<T2>(), glm_class_type, glm::RTTIConcat(#type, glm::getTypeName<T0>(), glm::getTypeName<T1>(), glm::getTypeName<T2>()), informationFunc)

//-------------------------------------------------------------------------
#define GLM_RTTI_T0_T1_T2_T3(typeID, type, T0, T1, T2, T3, informationFunc) \
    typedef type<T0, T1, T2, T3> glm_class_type;                            \
    GLM_RTTI_NAME(glm::Uuid(typeID) + glm::getTypeID<T0>() + glm::getTypeID<T1>() + glm::getTypeID<T2>() + glm::getTypeID<T3>(), glm_class_type, glm::RTTIConcat(#type, glm::getTypeName<T0>(), glm::getTypeName<T1>(), glm::getTypeName<T2>(), glm::getTypeName<T3>()), informationFunc)

//-------------------------------------------------------------------------
#define GLM_RTTI_T_NT(typeID, type, T, NT, informationFunc) \
    typedef type<T, NT> glm_class_type;                     \
    GLM_RTTI_NAME(glm::Uuid(typeID) + glm::getTypeID<T>() + NT, glm_class_type, glm::RTTIConcat(#type, glm::getTypeName<T>(), glm::toString(NT).c_str()), informationFunc)

#define GLM_ABSTRACT_RTTI_T_NT(typeID, type, T, NT, informationFunc) \
    typedef type<T, NT> glm_class_type;                              \
    GLM_ABSTRACT_RTTI_NAME(glm::Uuid(typeID) + glm::getTypeID<T>() + NT, glm_class_type, glm::RTTIConcat(#type, glm::getTypeName<T>(), glm::toString(NT).c_str()), informationFunc)

#define GLM_VIRTUAL_RTTI_T_NT(typeID, type, T, NT, informationFunc) \
    typedef type<T, NT> glm_class_type;                             \
    GLM_VIRTUAL_RTTI_NAME(glm::Uuid(typeID) + glm::getTypeID<T>() + NT, glm_class_type, glm::RTTIConcat(#type, glm::getTypeName<T>(), glm::toString(NT).c_str()), informationFunc)

//-------------------------------------------------------------------------
#define GLM_RTTI_T_NT0_NT1(typeID, type, T, NT0, NT1, informationFunc) \
    typedef type<T, NT0, NT1> glm_class_type;                          \
    GLM_RTTI_NAME(glm::Uuid(typeID) + glm::getTypeID<T>() + NT0 + NT1, glm_class_type, glm::RTTIConcat(#type, glm::getTypeName<T0>(), glm::getTypeName<T1>(), glm::toString(NT).c_str()), informationFunc)

#include "glmAbstractAttribute.h"

//-------------------------------------------------------------------------
template <class TAttribute>
const TAttribute* glm::RTTI::getAttribute(unsigned int attributeNumber) const
{
    const glm::AbstractAttribute* const attribute(getAttribute(attributeNumber));
    if (attribute == NULL)
        return NULL;
    GLM_DEBUG_ASSERT(TAttribute::getAttributeClassRTTI().getTypeID() == attribute->getAttributeRTTI().getTypeID());
    return (static_cast<const TAttribute* const>(attribute));
}

//-------------------------------------------------------------------------
template <class TAttribute>
const TAttribute* glm::RTTI::getAttribute(const char* name) const
{
    const glm::AbstractAttribute* const attribute(getAttribute(name));
    if (attribute == NULL)
        return NULL;
    GLM_DEBUG_ASSERT(TAttribute::getAttributeClassRTTI().getTypeID() == attribute->getAttributeRTTI().getTypeID());
    return (static_cast<const TAttribute* const>(attribute));
}

#include "glmAbstractAttribute.h"

//-------------------------------------------------------------------------
template <class TAttribute>
inline void glm::RTTI::addAttribute(const TAttribute& attribute) const
{
    if (!_removingAttributes)
    {
        // insure that attribute class RTTI is init.
        TAttribute::createAttributeRTTI();
        GLM_DEBUG_ASSERT_MSG(getAttribute(attribute.getName()) == NULL, "Attribute %s already registered", attribute.getName());
        TAttribute* newAttribute = new TAttribute(attribute); //NOTE: replaces "new TAttribute(attribute)"
        _attributes.push_back(newAttribute);
    }
    else
    {
        // find attribute with the given name(in this object attributes only, not in parent classes)
        GLM_DEBUG_ASSERT(strlen(attribute.getName()) < 1024); // How can an attribute name be more than 1024bytes?
        char* usedName = (char*)GLM_ALLOCA(1024 * sizeof(char));
        alphaNumerizeNameWithCopy(attribute.getName(), usedName);

        glm::PODArray<AbstractAttribute*>::iterator ite(_attributes.begin());
        // look for the queried attribute in this instance.
        for (; ite != _attributes.end(); ++ite)
        {
            if (std::strcmp(usedName, (*ite)->getName()) == 0)
            {
                break;
            }
        }
        GLM_DEBUG_ASSERT_MSG(ite != _attributes.end(), "Attribute %s already deregistered", attribute.getName());
        delete *ite;
        _attributes.erase(ite);
        TAttribute::destroyAttributeRTTI(); // call destroyAttributeRTTI -> destroyClassRTTI to keep counter up to date
    }
}
