// ReflectionMacros.h
//------------------------------------------------------------------------------
#pragma once

// Includes
//------------------------------------------------------------------------------
#include "Core/Reflection/MetaData/MetaData.h"
#include "Core/Reflection/PropertyType.h"
#include "Core/Reflection/ReflectionInfo.h"

#include <stddef.h>

// Forward Declarations
//------------------------------------------------------------------------------
class ReflectionInfo;

// Reflection
//------------------------------------------------------------------------------

// DECLARE
//------------------------------------------------------------------------------
#define REFLECT_STRUCT_DECLARE( structName ) \
    private: \
        friend class structName##_ReflectionInfo; \
        static const ReflectionInfo * s_ReflectionInfo; \
    public: \
        static const ReflectionInfo * GetReflectionInfoS();

#define REFLECT_DECLARE( className ) \
    REFLECT_STRUCT_DECLARE( className ) \
        virtual const ReflectionInfo * GetReflectionInfoV() const override;

// HELPER
//------------------------------------------------------------------------------
#define CHECK_BASE_CLASS( className, baseClass ) \
    const className * c = nullptr; \
    const baseClass * b = static_cast<const baseClass *>( c ); \
    (void)b;

#define MEMBER_PTR( member ) static_cast<decltype( objectType::member ) *>( nullptr )

// BEGIN
//------------------------------------------------------------------------------
#define REFLECT_BEGIN_COMMON( className, baseClass, structSize, isAbstract, ... ) \
    class baseClass##_ReflectionInfo; \
    extern baseClass##_ReflectionInfo g_##baseClass##_ReflectionInfo; \
    class className##_ReflectionInfo; \
    extern className##_ReflectionInfo g_##className##_ReflectionInfo; \
    const ReflectionInfo * className::s_ReflectionInfo( nullptr ); \
    const ReflectionInfo * className::GetReflectionInfoS() \
    { \
        PRAGMA_DISABLE_PUSH_MSVC( 4946 ) \
        return reinterpret_cast<const ReflectionInfo *>( &g_##className##_ReflectionInfo ); \
        PRAGMA_DISABLE_POP_MSVC \
    } \
    class className##_ReflectionInfo : public ReflectionInfo \
    { \
    public: \
        typedef className objectType; \
        explicit className##_ReflectionInfo() \
        { \
            SetTypeName( #className ); \
            className::s_ReflectionInfo = this; \
            AddProperties(); \
            m_StructSize = structSize; \
            m_IsAbstract = isAbstract; \
            m_SuperClass = reinterpret_cast<const ReflectionInfo *>( &g_##baseClass##_ReflectionInfo ); \
            AddMetaData( __VA_ARGS__ ); \
        } \
        virtual ~className##_ReflectionInfo() override\
        { \
            className::s_ReflectionInfo = nullptr; \
        }

#define REFLECT_BEGIN_ABSTRACT( className, baseClass, ... ) \
    const ReflectionInfo * className::GetReflectionInfoV() const \
    { \
        return className::GetReflectionInfoS(); \
    } \
    REFLECT_BEGIN_COMMON( className, baseClass, 0, true, __VA_ARGS__ ) \
    void AddProperties() \
    { \
        CHECK_BASE_CLASS( className, baseClass )

#define REFLECT_BEGIN( className, baseClass, ... ) \
    const ReflectionInfo * className::GetReflectionInfoV() const \
    { \
        return className::GetReflectionInfoS(); \
    } \
    REFLECT_BEGIN_COMMON( className, baseClass, 0, false, __VA_ARGS__ ) \
        void AddProperties() \
        { \
            CHECK_BASE_CLASS( className, baseClass )

#define REFLECT_STRUCT_BEGIN( structName, baseStruct, ... ) \
    REFLECT_BEGIN_COMMON( structName, baseStruct, sizeof( structName ), false, __VA_ARGS__ ) \
        virtual void SetArraySizeV( void * array, size_t size ) const override \
        { \
            Array<structName> * realArray = static_cast<Array<structName> *>( array ); \
            realArray->SetSize( size ); \
        } \
        void AddProperties() \
        { \
            CHECK_BASE_CLASS( structName, baseStruct )

#define REFLECT_STRUCT_BEGIN_ABSTRACT( structName, baseStruct, ... ) \
    REFLECT_BEGIN_COMMON( structName, baseStruct, sizeof( structName ), false, __VA_ARGS__ ) \
        void AddProperties() \
        { \
            CHECK_BASE_CLASS( structName, baseStruct )

#define REFLECT_STRUCT_BEGIN_BASE( structName ) \
    REFLECT_BEGIN_COMMON( structName, Struct, sizeof( structName ), false ) \
        virtual void SetArraySizeV( void * array, size_t size ) const override \
        { \
            Array<structName> * realArray = static_cast<Array<structName> *>( array ); \
            realArray->SetSize( size ); \
        } \
        void AddProperties() \
        {

// MEMBERS
//------------------------------------------------------------------------------
#define REFLECT( member, ... ) \
            { \
                constexpr auto memberName = #member; \
                static_assert( ( memberName[ 0 ] == 'm' ) && ( memberName[ 1 ] == '_' ) ); \
                AddProperty( offsetof( objectType, member ), \
                             ( memberName + 2 ), \
                             ::GetPropertyType( MEMBER_PTR( member ) ), \
                             ::IsArrayProperty( MEMBER_PTR( member ) ), \
                             ::GetStructType( MEMBER_PTR( member ) ) ); \
                AddPropertyMetaData( __VA_ARGS__ ); \
            }

#define REFLECT_RENAME( member, memberName, ... ) \
            { \
                AddProperty( offsetof( objectType, member ), \
                             memberName, \
                             ::GetPropertyType( MEMBER_PTR( member ) ), \
                             ::IsArrayProperty( MEMBER_PTR( member ) ), \
                             ::GetStructType( MEMBER_PTR( member ) ) ); \
                AddPropertyMetaData( __VA_ARGS__ ); \
            }

// END
//------------------------------------------------------------------------------
#define REFLECT_END( className ) \
        } \
    }; \
    className##_ReflectionInfo g_##className##_ReflectionInfo;

//------------------------------------------------------------------------------
