/////////////////////////////////////////////////////////////////////////////
// Name:        wx/xtitypes.h
// Purpose:     enum, set, basic types support
// Author:      Stefan Csomor
// Modified by: Francesco Montorsi
// Created:     27/07/03
// Copyright:   (c) 1997 Julian Smart
//              (c) 2003 Stefan Csomor
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

#ifndef _XTITYPES_H_
#define _XTITYPES_H_

#include "wx/defs.h"

#if wxUSE_EXTENDED_RTTI

#include "wx/string.h"
#include "wx/arrstr.h"
#include "wx/flags.h"
#include "wx/intl.h"
#include "wx/log.h"
#include <typeinfo>
#include <unordered_map>

class WXDLLIMPEXP_BASE wxClassInfo;

// ----------------------------------------------------------------------------
// Enum Support
//
// In the header files XTI requires no change from pure c++ code, however in the
// implementation, an enum needs to be enumerated e.g.:
//
// wxBEGIN_ENUM( wxFlavor )
//   wxENUM_MEMBER( Vanilla )
//   wxENUM_MEMBER( Chocolate )
//   wxENUM_MEMBER( Strawberry )
// wxEND_ENUM( wxFlavor )
// ----------------------------------------------------------------------------

struct WXDLLIMPEXP_BASE wxEnumMemberData
{
    const wxChar*   m_name;
    int             m_value;
};

class WXDLLIMPEXP_BASE wxEnumData
{
public:
    wxEnumData( wxEnumMemberData* data );

    // returns true if the member has been found and sets the int value
    // pointed to accordingly (if ptr != null )
    // if not found returns false, value left unchanged
    bool HasEnumMemberValue( const wxChar *name, int *value = nullptr ) const;

    // returns the value of the member, if not found in debug mode an
    // assert is issued, in release 0 is returned
    int GetEnumMemberValue(const wxChar *name ) const;

    // returns the name of the enum member having the passed in value
    // returns an empty string if not found
    const wxChar *GetEnumMemberName(int value) const;

    // returns the number of members in this enum
    int GetEnumCount() const { return m_count; }

    // returns the value of the nth member
    int GetEnumMemberValueByIndex( int n ) const;

    // returns the value of the nth member
    const wxChar *GetEnumMemberNameByIndex( int n ) const;

private:
    wxEnumMemberData *m_members;
    int m_count;
};

#define wxBEGIN_ENUM( e ) \
    wxEnumMemberData s_enumDataMembers##e[] = {

#define wxENUM_MEMBER( v ) { wxT(#v), v },

#define wxEND_ENUM( e )                                                 \
        { nullptr, 0 } };                                                 \
    wxEnumData s_enumData##e( s_enumDataMembers##e );                   \
    wxEnumData *wxGetEnumData(e) { return &s_enumData##e; }             \
    template<> void wxStringReadValue(const wxString& s, e &data )     \
        { data = (e) s_enumData##e.GetEnumMemberValue(s.c_str()); }     \
    template<> void wxStringWriteValue(wxString &s, const e &data )    \
        { s = s_enumData##e.GetEnumMemberName((int)data); }             \
    void FromLong##e( long data, wxAny& result )                  \
        { result = wxAny((e)data); }                               \
    void ToLong##e( const wxAny& data, long &result )             \
        { result = (long) (data).As(static_cast<e*>(nullptr)); }      \
                                                                        \
    wxTO_STRING_IMP( e )                                                \
    wxFROM_STRING_IMP( e )                                              \
    wxEnumTypeInfo s_typeInfo##e(wxT_ENUM, &s_enumData##e,            \
            &wxTO_STRING( e ), &wxFROM_STRING( e ), &ToLong##e,      \
            &FromLong##e, typeid(e).name() );


// ----------------------------------------------------------------------------
// Set Support
//
// in the header :
//
// enum wxFlavor
// {
//  Vanilla,
//  Chocolate,
//  Strawberry,
// };
//
// typedef wxBitset<wxFlavor> wxCoupe;
//
// in the implementation file :
//
// wxBEGIN_ENUM( wxFlavor )
//  wxENUM_MEMBER( Vanilla )
//  wxENUM_MEMBER( Chocolate )
//  wxENUM_MEMBER( Strawberry )
// wxEND_ENUM( wxFlavor )
//
// wxIMPLEMENT_SET_STREAMING( wxCoupe, wxFlavor )
//
// implementation note: no partial specialization for streaming, but a delegation
//                      to a different class
//
// ----------------------------------------------------------------------------

void WXDLLIMPEXP_BASE wxSetStringToArray( const wxString &s, wxArrayString &array );

template<typename e>
void wxSetFromString(const wxString &s, wxBitset<e> &data )
{
    wxEnumData* edata = wxGetEnumData((e) 0);
    data.reset();

    wxArrayString array;
    wxSetStringToArray( s, array );
    wxString flag;
    for ( int i = 0; i < array.Count(); ++i )
    {
        flag = array[i];
        int ivalue;
        if ( edata->HasEnumMemberValue( flag.c_str(), &ivalue ) )
        {
            data.set( (e) ivalue );
        }
    }
}

template<typename e>
void wxSetToString( wxString &s, const wxBitset<e> &data )
{
    wxEnumData* edata = wxGetEnumData((e) 0);
    int count = edata->GetEnumCount();
    int i;
    s.Clear();
    for ( i = 0; i < count; i++ )
    {
        e value = (e) edata->GetEnumMemberValueByIndex(i);
        if ( data.test( value ) )
        {
            // this could also be done by the templated calls
            if ( !s.empty() )
                s += wxT("|");
            s += edata->GetEnumMemberNameByIndex(i);
        }
    }
}

#define wxIMPLEMENT_SET_STREAMING(SetName,e)                                    \
    template<> void wxStringReadValue(const wxString &s, wxBitset<e> &data )   \
        { wxSetFromString( s, data ); }                                        \
    template<> void wxStringWriteValue( wxString &s, const wxBitset<e> &data ) \
        { wxSetToString( s, data ); }                                          \
    void FromLong##SetName( long data, wxAny& result )                    \
        { result = wxAny(SetName((unsigned long)data)); }                  \
    void ToLong##SetName( const wxAny& data, long &result )               \
        { result = (long) (data).As(static_cast<SetName*>(nullptr)).to_ulong(); } \
    wxTO_STRING_IMP( SetName )                                                  \
    wxFROM_STRING_IMP( SetName )                                                \
    wxEnumTypeInfo s_typeInfo##SetName(wxT_SET, &s_enumData##e,               \
            &wxTO_STRING( SetName ), &wxFROM_STRING( SetName ),               \
            &ToLong##SetName, &FromLong##SetName, typeid(SetName).name() );

template<typename e>
void wxFlagsFromString(const wxString &s, e &data )
{
    wxEnumData* edata = wxGetEnumData((e*) 0);
    data.m_data = 0;

    wxArrayString array;
    wxSetStringToArray( s, array );
    wxString flag;
    for ( size_t i = 0; i < array.Count(); ++i )
    {
        flag = array[i];
        int ivalue;
        if ( edata->HasEnumMemberValue( flag.c_str(), &ivalue ) )
        {
            data.m_data |= ivalue;
        }
    }
}

template<typename e>
void wxFlagsToString( wxString &s, const e& data )
{
    wxEnumData* edata = wxGetEnumData((e*) 0);
    int count = edata->GetEnumCount();
    int i;
    s.Clear();
    long dataValue = data.m_data;
    for ( i = 0; i < count; i++ )
    {
        int value = edata->GetEnumMemberValueByIndex(i);
        // make this to allow for multi-bit constants to work
        if ( value && ( dataValue & value ) == value )
        {
            // clear the flags we just set
            dataValue &= ~value;
            // this could also be done by the templated calls
            if ( !s.empty() )
                s +=wxT("|");
            s += edata->GetEnumMemberNameByIndex(i);
        }
    }
}

#define wxBEGIN_FLAGS( e ) \
    wxEnumMemberData s_enumDataMembers##e[] = {

#define wxFLAGS_MEMBER( v ) { wxT(#v), static_cast<int>(v) },

#define wxEND_FLAGS( e )                                                \
        { nullptr, 0 } };                                                 \
    wxEnumData s_enumData##e( s_enumDataMembers##e );                   \
    wxEnumData *wxGetEnumData(e*) { return &s_enumData##e; }            \
    template<>  void wxStringReadValue(const wxString &s, e &data )    \
        { wxFlagsFromString<e>( s, data ); }                           \
    template<>  void wxStringWriteValue( wxString &s, const e& data )  \
        { wxFlagsToString<e>( s, data ); }                             \
    void FromLong##e( long data, wxAny& result )                  \
        { result = wxAny(e(data)); }                               \
    void ToLong##e( const wxAny& data, long &result )             \
        { result = (long) (data).As(static_cast<e*>(nullptr)).m_data; } \
    wxTO_STRING_IMP( e )                                                \
    wxFROM_STRING_IMP( e )                                              \
    wxEnumTypeInfo s_typeInfo##e(wxT_SET, &s_enumData##e,             \
            &wxTO_STRING( e ), &wxFROM_STRING( e ), &ToLong##e,      \
            &FromLong##e, typeid(e).name() );

// ----------------------------------------------------------------------------
// Type Information
// ----------------------------------------------------------------------------

//  All data exposed by the RTTI is characterized using the following classes.
//  The first characterization is done by wxTypeKind. All enums up to and including
//  wxT_CUSTOM represent so called simple types. These cannot be divided any further.
//  They can be converted to and from wxStrings, that's all.
//  Other wxTypeKinds can instead be split recursively into smaller parts until
//  the simple types are reached.

enum wxTypeKind
{
    wxT_VOID = 0,   // unknown type
    wxT_BOOL,
    wxT_CHAR,
    wxT_UCHAR,
    wxT_INT,
    wxT_UINT,
    wxT_LONG,
    wxT_ULONG,
    wxT_LONGLONG,
    wxT_ULONGLONG,
    wxT_FLOAT,
    wxT_DOUBLE,
    wxT_STRING,     // must be wxString
    wxT_SET,        // must be wxBitset<> template
    wxT_ENUM,
    wxT_CUSTOM,     // user defined type (e.g. wxPoint)

    wxT_LAST_SIMPLE_TYPE_KIND = wxT_CUSTOM,

    wxT_OBJECT_PTR, // object reference
    wxT_OBJECT,     // embedded object
    wxT_COLLECTION, // collection

    wxT_DELEGATE,   // for connecting against an event source

    wxT_LAST_TYPE_KIND = wxT_DELEGATE // sentinel for bad data, asserts, debugging
};

class WXDLLIMPEXP_BASE wxAny;
class WXDLLIMPEXP_BASE wxTypeInfo;

using wxTypeInfo = std::unordered_map<wxString, wxTypeInfo*>;

class WXDLLIMPEXP_BASE wxTypeInfo
{
public:
    typedef void (*wxVariant2StringFnc)( const wxAny& data, wxString &result );
    typedef void (*wxString2VariantFnc)( const wxString& data, wxAny &result );

    wxTypeInfo(wxTypeKind kind,
               wxVariant2StringFnc to = nullptr, wxString2VariantFnc from = nullptr,
               const wxString &name = wxEmptyString):
            m_toString(to), m_fromString(from), m_kind(kind), m_name(name)
    {
        Register();
    }
#if 0
    wxTypeInfo(wxTypeKind kind,
               wxVariant2StringFnc to, wxString2VariantFnc from,
               const char *name):
            m_toString(to), m_fromString(from), m_kind(kind),
            m_name(wxString::FromAscii(name))
    {
        Register();
    }
#endif

    virtual ~wxTypeInfo()
    {
        Unregister();
    }

    // return the kind of this type (wxT_... constants)
    wxTypeKind GetKind() const { return m_kind; }

    // returns the unique name of this type
    const wxString& GetTypeName() const { return m_name; }

    // is this type a delegate type
    bool IsDelegateType() const { return m_kind == wxT_DELEGATE; }

    // is this type a custom type
    bool IsCustomType() const { return m_kind == wxT_CUSTOM; }

    // is this type an object type
    bool IsObjectType() const { return m_kind == wxT_OBJECT || m_kind == wxT_OBJECT_PTR; }

    // can the content of this type be converted to and from strings ?
    bool HasStringConverters() const { return m_toString != nullptr && m_fromString != nullptr; }

    // convert a wxAny holding data of this type into a string
    void ConvertToString( const wxAny& data, wxString &result ) const
    {
        if ( m_toString )
            (*m_toString)( data, result );
        else
            wxLogError( wxGetTranslation(wxT("String conversions not supported")) );
    }

    // convert a string into a wxAny holding the corresponding data in this type
    void ConvertFromString( const wxString& data, wxAny &result ) const
    {
        if( m_fromString )
            (*m_fromString)( data, result );
        else
            wxLogError( wxGetTranslation(wxT("String conversions not supported")) );
    }

    // statics:

    // looks for the corresponding type, will return nullptr if not found
    static wxTypeInfo *FindType( const wxString& typeName );
private:
    void Register();
    void Unregister();

    wxVariant2StringFnc m_toString;
    wxString2VariantFnc m_fromString;

    wxTypeKind m_kind;
    wxString m_name;

    // the static list of all types we know about
    static wxTypeInfoMap* ms_typeTable;
};

class WXDLLIMPEXP_BASE wxBuiltInTypeInfo : public wxTypeInfo
{
public:
    wxBuiltInTypeInfo( wxTypeKind kind, wxVariant2StringFnc to = nullptr,
                       wxString2VariantFnc from = nullptr,
                       const wxString &name = wxEmptyString ) :
            wxTypeInfo( kind, to, from, name )
       { wxASSERT_MSG( GetKind() < wxT_SET, wxT("Illegal Kind for Base Type") ); }
};

class WXDLLIMPEXP_BASE wxCustomTypeInfo : public wxTypeInfo
{
public:
    wxCustomTypeInfo( const wxString &name, wxVariant2StringFnc to,
                      wxString2VariantFnc from ) :
            wxTypeInfo( wxT_CUSTOM, to, from, name )
       {}
};

class WXDLLIMPEXP_BASE wxEnumTypeInfo : public wxTypeInfo
{
public:
    typedef void (*converterToLong_t)( const wxAny& data, long &result );
    typedef void (*converterFromLong_t)( long data, wxAny &result );

    wxEnumTypeInfo( wxTypeKind kind, wxEnumData* enumInfo, wxVariant2StringFnc to,
                    wxString2VariantFnc from, converterToLong_t toLong,
                    converterFromLong_t fromLong, const wxString &name  ) :
        wxTypeInfo( kind, to, from, name ), m_toLong( toLong ), m_fromLong( fromLong )
    {
        wxASSERT_MSG( kind == wxT_ENUM || kind == wxT_SET,
                      wxT("Illegal Kind for Enum Type"));
        m_enumInfo = enumInfo;
    }

    const wxEnumData* GetEnumData() const { return m_enumInfo; }

    // convert a wxAny holding data of this type into a long
    void ConvertToLong( const wxAny& data, long &result ) const
    {
        if( m_toLong )
            (*m_toLong)( data, result );
        else
            wxLogError( wxGetTranslation(wxT("Long Conversions not supported")) );
    }

    // convert a long into a wxAny holding the corresponding data in this type
    void ConvertFromLong( long data, wxAny &result ) const
    {
        if( m_fromLong )
            (*m_fromLong)( data, result );
        else
            wxLogError( wxGetTranslation(wxT("Long Conversions not supported")) );
    }

private:
    converterToLong_t m_toLong;
    converterFromLong_t m_fromLong;

    wxEnumData *m_enumInfo; // Kind == wxT_ENUM or Kind == wxT_SET
};

class WXDLLIMPEXP_BASE wxClassTypeInfo : public wxTypeInfo
{
public:
    wxClassTypeInfo( wxTypeKind kind, wxClassInfo* classInfo,
                     wxVariant2StringFnc to = nullptr, wxString2VariantFnc from = nullptr,
                     const wxString &name = wxEmptyString);

    const wxClassInfo *GetClassInfo() const { return m_classInfo; }

private:
    wxClassInfo *m_classInfo; // Kind == wxT_OBJECT - could be null
};

class WXDLLIMPEXP_BASE wxCollectionTypeInfo : public wxTypeInfo
{
public:
    wxCollectionTypeInfo( const wxString &elementName, wxVariant2StringFnc to,
                          wxString2VariantFnc from , const wxString &name) :
            wxTypeInfo( wxT_COLLECTION, to, from, name )
       { m_elementTypeName = elementName; m_elementType = nullptr; }

    const wxTypeInfo* GetElementType() const
    {
        if ( m_elementType == nullptr )
            m_elementType = wxTypeInfo::FindType( m_elementTypeName );
        return m_elementType;
    }

private:
    mutable wxTypeInfo * m_elementType;
    wxString    m_elementTypeName;
};

class WXDLLIMPEXP_BASE wxEventSourceTypeInfo : public wxTypeInfo
{
public:
    wxEventSourceTypeInfo( int eventType, wxClassInfo* eventClass,
                        wxVariant2StringFnc to = nullptr,
                        wxString2VariantFnc from = nullptr );
    wxEventSourceTypeInfo( int eventType, int lastEventType, wxClassInfo* eventClass,
                        wxVariant2StringFnc to = nullptr, wxString2VariantFnc from = nullptr );

    int GetEventType() const { return m_eventType; }
    int GetLastEventType() const { return m_lastEventType; }
    const wxClassInfo* GetEventClass() const { return m_eventClass; }

private:
    const wxClassInfo *m_eventClass; // (extended will merge into classinfo)
    int m_eventType;
    int m_lastEventType;
};

template<typename T> const wxTypeInfo* wxGetTypeInfo( T * )
    { return wxTypeInfo::FindType(typeid(T).name()); }

// this macro is for usage with custom, non-object derived classes and structs,
// wxPoint is such a custom type

#if wxUSE_FUNC_TEMPLATE_POINTER
    #define wxCUSTOM_TYPE_INFO( e, toString, fromString ) \
        wxCustomTypeInfo s_typeInfo##e(typeid(e).name(), &toString, &fromString);
#else
    #define wxCUSTOM_TYPE_INFO( e, toString, fromString )             \
        void ToString##e( const wxAny& data, wxString &result )   \
            { toString(data, result); }                                 \
        void FromString##e( const wxString& data, wxAny &result ) \
            { fromString(data, result); }                               \
        wxCustomTypeInfo s_typeInfo##e(typeid(e).name(),               \
                                       &ToString##e, &FromString##e);
#endif

#define wxCOLLECTION_TYPE_INFO( element, collection )                      \
    wxCollectionTypeInfo s_typeInfo##collection( typeid(element).name(),   \
                                nullptr, nullptr, typeid(collection).name() );

// sometimes a compiler invents specializations that are nowhere called,
// use this macro to satisfy the refs, currently we don't have to play
// tricks, but if we will have to according to the compiler, we will use
// that macro for that

#define wxILLEGAL_TYPE_SPECIALIZATION( a )

#endif      // wxUSE_EXTENDED_RTTI
#endif      // _XTITYPES_H_
