// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Log.h"
#include "Engine/Scripting/Scripting.h"
#if USE_CSHARP
#include "Engine/Scripting/BinaryModule.h"
#include "Engine/Scripting/ManagedCLR/MUtils.h"
#include "Engine/Scripting/ManagedCLR/MClass.h"
#include "Engine/Scripting/ManagedCLR/MMethod.h"
#include "Engine/Scripting/ManagedCLR/MAssembly.h"
#include "Engine/Scripting/ManagedCLR/MException.h"
#include "Engine/Scripting/Internal/StdTypesContainer.h"
///
/// Utility interop between C++ and C# for Dictionary collection.
///
struct FLAXENGINE_API ManagedDictionary
{
MObject* Instance;
ManagedDictionary(MObject* instance = nullptr)
{
Instance = instance;
}
template
static MObject* ToManaged(const Dictionary& data, MType* keyType, MType* valueType)
{
MConverter keysConverter;
MConverter valueConverter;
ManagedDictionary result = New(keyType, valueType);
MClass* keyClass = MCore::Type::GetClass(keyType);
MClass* valueClass = MCore::Type::GetClass(valueType);
for (auto i = data.Begin(); i.IsNotEnd(); ++i)
{
MObject* keyManaged = keysConverter.Box(i->Key, keyClass);
MObject* valueManaged = valueConverter.Box(i->Value, valueClass);
result.Add(keyManaged, valueManaged);
}
return result.Instance;
}
///
/// Converts the managed dictionary objects into the native dictionary collection.
///
/// The managed dictionary object.
/// The output array.
template
static Dictionary ToNative(MObject* managed)
{
Dictionary result;
const ManagedDictionary wrapper(managed);
MArray* managedKeys = wrapper.GetKeys();
if (managedKeys == nullptr)
return result;
int32 length = MCore::Array::GetLength(managedKeys);
Array keys;
keys.Resize(length);
result.EnsureCapacity(length);
MConverter keysConverter;
MConverter valueConverter;
MObject** managedKeysPtr = MCore::Array::GetAddress(managedKeys);
for (int32 i = 0; i < keys.Count(); i++)
{
KeyType& key = keys[i];
MObject* keyManaged = managedKeysPtr[i];
keysConverter.Unbox(key, keyManaged);
MObject* valueManaged = wrapper.GetValue(keyManaged);
ValueType& value = result[key];
valueConverter.Unbox(value, valueManaged);
}
return result;
}
static MTypeObject* GetClass(MType* keyType, MType* valueType)
{
MClass* scriptingClass = Scripting::GetStaticClass();
CHECK_RETURN(scriptingClass, nullptr);
MMethod* makeGenericMethod = scriptingClass->GetMethod("MakeGenericType", 2);
CHECK_RETURN(makeGenericMethod, nullptr);
MTypeObject* genericType = MUtils::GetType(StdTypesContainer::Instance()->DictionaryClass);
#if USE_NETCORE
MArray* genericArgs = MCore::Array::New(MCore::TypeCache::IntPtr, 2);
#else
MArray* genericArgs = MCore::Array::New(MCore::TypeCache::Object, 2);
#endif
MTypeObject** genericArgsPtr = MCore::Array::GetAddress(genericArgs);
genericArgsPtr[0] = INTERNAL_TYPE_GET_OBJECT(keyType);
genericArgsPtr[1] = INTERNAL_TYPE_GET_OBJECT(valueType);
void* params[2];
params[0] = genericType;
params[1] = genericArgs;
MObject* exception = nullptr;
MObject* dictionaryType = makeGenericMethod->Invoke(nullptr, params, &exception);
if (exception)
{
MException ex(exception);
ex.Log(LogType::Error, TEXT(""));
return nullptr;
}
return (MTypeObject*)dictionaryType;
}
static ManagedDictionary New(MType* keyType, MType* valueType)
{
ManagedDictionary result;
MTypeObject* dictionaryType = GetClass(keyType, valueType);
if (!dictionaryType)
return result;
MClass* scriptingClass = Scripting::GetStaticClass();
CHECK_RETURN(scriptingClass, result);
MMethod* createMethod = StdTypesContainer::Instance()->ActivatorClass->GetMethod("CreateInstance", 2);
CHECK_RETURN(createMethod, result);
MObject* exception = nullptr;
void* params[2];
params[0] = dictionaryType;
params[1] = nullptr;
MObject* instance = createMethod->Invoke(nullptr, params, &exception);
if (exception)
{
MException ex(exception);
ex.Log(LogType::Error, TEXT(""));
return result;
}
result.Instance = instance;
return result;
}
void Add(MObject* key, MObject* value)
{
CHECK(Instance);
MClass* scriptingClass = Scripting::GetStaticClass();
CHECK(scriptingClass);
MMethod* addDictionaryItemMethod = scriptingClass->GetMethod("AddDictionaryItem", 3);
CHECK(addDictionaryItemMethod);
void* params[3];
params[0] = Instance;
params[1] = key;
params[2] = value;
MObject* exception = nullptr;
addDictionaryItemMethod->Invoke(Instance, params, &exception);
if (exception)
{
MException ex(exception);
ex.Log(LogType::Error, TEXT(""));
}
}
MArray* GetKeys() const
{
CHECK_RETURN(Instance, nullptr);
MClass* scriptingClass = Scripting::GetStaticClass();
CHECK_RETURN(scriptingClass, nullptr);
MMethod* getDictionaryKeysMethod = scriptingClass->GetMethod("GetDictionaryKeys", 1);
CHECK_RETURN(getDictionaryKeysMethod, nullptr);
void* params[1];
params[0] = Instance;
return (MArray*)getDictionaryKeysMethod->Invoke( nullptr, params, nullptr);
}
MObject* GetValue(MObject* key) const
{
CHECK_RETURN(Instance, nullptr);
MClass* klass = MCore::Object::GetClass(Instance);
MMethod* getItemMethod = klass->GetMethod("System.Collections.IDictionary.get_Item", 1);
CHECK_RETURN(getItemMethod, nullptr);
void* params[1];
params[0] = key;
return getItemMethod->Invoke(Instance, params, nullptr);
}
};
#endif