// Copyright (c) 2024 Codemongous LLC. All Rights Reserved.

#include "SCW_UnrealEngine.h"

#include "Algo/LevenshteinDistance.h"
#include "AnimationGraph.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "Blueprint/WidgetBlueprintGeneratedClass.h"
#include "BlueprintNamespaceUtilities.h"
#include "Components/WidgetComponent.h"
#include "DesktopPlatformModule.h"
#include "Editor/EditorPerProjectUserSettings.h"
#include "Engine/InheritableComponentHandler.h"
#include "Engine/UserDefinedStruct.h"
#include "Engine/World.h"
#include "FileHelpers.h"
#include "GameProjectUtils.h"
#include "GeneralProjectSettings.h"
#include "GenericPlatform/GenericPlatformMisc.h"
#include "HAL/PlatformFileManager.h"
#include "ILiveCodingModule.h"
#include "ISourceControlModule.h"
#include "ISourceControlProvider.h"
#include "K2Node_FunctionResult.h"
#include "K2Node_FunctionTerminator.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "Kismet2/StructureEditorUtils.h"
#include <memory>
#include "Misc/CommandLine.h"
#include "Misc/ConfigCacheIni.h"
#include "Misc/FileHelper.h"
#include "Misc/MessageDialog.h"
#include "Modules/ModuleManager.h"
#include "Subsystems/AssetEditorSubsystem.h"
#include "ObjectTools.h"
#include "SCW_AActor.h"
#include "SCW_FProperty.h"
#include "SCW_FSubobjectDataHandle.h"
#include "SCW_UBlueprint.h"
#include "SCW_UBlueprintGeneratedClass.h"
#include "SCW_UClass.h"
#include "SCW_UEdGraph.h"
#include "SCW_UEdGraphNode.h"
#include "SCW_UFunction.h"
#include "SCW_UK2Node_AddComponent.h"
#include "SCW_UObject.h"
#include "SCW_UPackage.h"
#include "SCW_USceneComponent.h"
#include "SCW_USCS_Node.h"
#include "SCW_UStruct.h"
#include "SCW_UUserDefinedStruct.h"
#include "Serialization/Csv/CsvParser.h"
#include "SourceCodeNavigation.h"
#include "SourceControlOperations.h"
#include "SourcifyCoreUEWrapperModule.h"
#include "SubobjectDataSubsystem.h"
#include "UObject/MetaData.h"
#include "UObject/UnrealType.h"
#include "UObject/UObjectGlobals.h"

using std::const_pointer_cast;

using namespace Sourcify;

DEFINE_LOG_CATEGORY_STATIC(LogSourcifyBPPostProcess, Log, All);
DEFINE_LOG_CATEGORY_STATIC(LogSourcifyCore, Log, All);
DEFINE_LOG_CATEGORY_STATIC(LogSourcifyCoreModule, Log, All);
DEFINE_LOG_CATEGORY_STATIC(LogSourcifyCoreModuleUtilities, Log, All);
DEFINE_LOG_CATEGORY_STATIC(LogSourcifyLicenseManager, Log, All);

extern ENGINE_API bool GFlushRenderingCommandsOnPreEditChange;

SCW_UnrealEngine::SCW_UnrealEngine()
{

}

int32_t SCW_UnrealEngine::Algo_LevenshteinDistance(const wstring& StrA, const wstring& StrB) const
{
	FString StringA = StrA.c_str();
	FString StringB = StrB.c_str();
	return Algo::LevenshteinDistance(StringA, StringB);
}

void SCW_UnrealEngine::EditorUtilities_CopySingleProperty(ISCW_UObjectConstPtr InSourceObject, ISCW_UObjectPtr InTargetObject, ISCW_FPropertyConstPtr InProperty) const
{
	const SCW_UObject* SCW_SourceObjectPtr = dynamic_cast<const SCW_UObject*>(InSourceObject.get());
	SCW_UObject* SCW_TargetObjectPtr = dynamic_cast<SCW_UObject*>(InTargetObject.get());
	const SCW_FProperty* SCW_PropertyPtr = dynamic_cast<const SCW_FProperty*>(InProperty.get());
	EditorUtilities::CopySingleProperty(SCW_SourceObjectPtr->ToUObjectRawPtr(), SCW_TargetObjectPtr->ToUObjectRawPtr(), SCW_PropertyPtr->ToFProperty());
}

ISCW_UActorComponentPtr SCW_UnrealEngine::EditorUtilities_FindMatchingComponentInstance(ISCW_UActorComponentPtr SourceComponent, ISCW_AActorPtr TargetActor) const
{
	SCW_UActorComponent* SCW_SourceComponentPtr = dynamic_cast<SCW_UActorComponent*>(SourceComponent.get());
	AActor* TargetActorPtr = dynamic_cast<SCW_AActor*>(TargetActor.get())->ToAActor();
	if (UActorComponent* Component = EditorUtilities::FindMatchingComponentInstance(SCW_SourceComponentPtr->ToUActorComponent(), TargetActorPtr))
	{
		return FSourcifyCoreUEWrapper::Get().Wrap(*Component);
	}

	return nullptr;
}

void SCW_UnrealEngine::FBlueprintNamespaceUtilities_GetDefaultImportsForObject(ISCW_UObjectConstPtr InObject, set<wstring>& OutNamespaces) const
{
	TSet<FString> FStringOutNamespaces;
	FBlueprintNamespaceUtilities::GetDefaultImportsForObject(dynamic_cast<const SCW_UObject*>(InObject.get())->ToUObjectRawPtr(), FStringOutNamespaces);
	
	for (const FString& Namespace : FStringOutNamespaces)
	{
		OutNamespaces.insert(*Namespace);
	}
}

void SCW_UnrealEngine::FBlueprintEditorUtils_PurgeNullGraphs(ISCW_UBlueprintPtr Blueprint) const
{
	FBlueprintEditorUtils::PurgeNullGraphs(dynamic_cast<SCW_UBlueprint*>(Blueprint.get())->ToUBlueprintRawPtr());
}

bool SCW_UnrealEngine::FBlueprintEditorUtils_SupportsConstructionScript(ISCW_UBlueprintPtr Blueprint) const
{
	return FBlueprintEditorUtils::SupportsConstructionScript(dynamic_cast<SCW_UBlueprint*>(Blueprint.get())->ToUBlueprintRawPtr());
}

ISCW_FComponentKeyPtr SCW_UnrealEngine::FComponentKey(ISCW_UBlueprintPtr Blueprint, ISCW_UK2Node_AddComponentPtr SCSNode) const
{
	SCW_UBlueprint* SCW_UBlueprintPtr = dynamic_cast<SCW_UBlueprint*>(Blueprint.get());
	SCW_UK2Node_AddComponent* SCW_AddComponent_NodePtr = dynamic_cast<SCW_UK2Node_AddComponent*>(SCSNode.get());
	::FComponentKey Key{ SCW_UBlueprintPtr->ToUBlueprintRawPtr(), FUCSComponentId(SCW_AddComponent_NodePtr->ToUK2Node_AddComponent()) };
	return FSourcifyCoreUEWrapper::Get().Wrap(Key);
}

ISCW_FComponentKeyPtr SCW_UnrealEngine::FComponentKey(ISCW_USCS_NodePtr SCSNode) const
{
	SCW_USCS_Node* SCW_SCS_NodePtr = dynamic_cast<SCW_USCS_Node*>(SCSNode.get());
	::FComponentKey Key{ SCW_SCS_NodePtr->ToUSCS_Node() };
	return FSourcifyCoreUEWrapper::Get().Wrap(Key);
}

wstring SCW_UnrealEngine::FGuid_NewGuid() const
{
	return *FGuid::NewGuid().ToString();
}

ISCW_UObjectPtr SCW_UnrealEngine::FindObjectWithOuter(ISCW_UObjectConstPtr Outer, SCW_ClassToLookFor ClassToLookFor/* = SCW_ClassToLookFor::None*/, wstring NameToLookFor/* = L"None"*/ /*NAME_None*/) const
{
	FName Name{ NameToLookFor.c_str() };
	UObject* Object = dynamic_cast<const SCW_UObject*>(Outer.get())->ToUObjectRawPtr();
	
	switch (ClassToLookFor)
	{
	default:
	case SCW_ClassToLookFor::None:
	{
		UObjectBase* OuterObject = ::FindObjectWithOuter(Object, nullptr, Name);
		return OuterObject ? FSourcifyCoreUEWrapper::Get().Wrap(*static_cast<UObject*>(OuterObject)) : nullptr;
	}
	case SCW_ClassToLookFor::UActorComponent:
	{
		UObjectBase* OuterObject = ::FindObjectWithOuter(Object, UActorComponent::StaticClass(), Name);
		return OuterObject ? FSourcifyCoreUEWrapper::Get().Wrap(*CastChecked<UActorComponent>(static_cast<UObject*>(OuterObject))) : nullptr;
	}
	}
}

void SCW_UnrealEngine::FStructureEditorUtils_CompileStructure(ISCW_UUserDefinedStructPtr Struct) const
{
	if (Struct != nullptr)
	{
		FStructureEditorUtils::CompileStructure(dynamic_cast<SCW_UUserDefinedStruct*>(Struct.get())->ToUUserDefinedStruct());
	}
}

void SCW_UnrealEngine::GEngine_Exec(const wchar_t* Command) const
{
	GEngine->Exec(GWorld, Command);
}

ISCW_UGeneralProjectSettingsConstPtr SCW_UnrealEngine::GetDefault_UGeneralProjectSettings() const
{
	return FSourcifyCoreUEWrapper::Get().Wrap(*GetMutableDefault<UGeneralProjectSettings>());
}

void SCW_UnrealEngine::GetDerivedClasses(ISCW_UClassConstPtr ClassToLookFor, vector<ISCW_UClassPtr>& Results, bool bRecursive/* = true*/) const
{
	TArray<UClass*> DerivedClasses;
	UClass* Class = dynamic_cast<const SCW_UClass*>(ClassToLookFor.get())->ToUClass();
	::GetDerivedClasses(Class, DerivedClasses, bRecursive);

	Results.clear();
	for (UClass* DerivedClass : DerivedClasses)
	{
		Results.push_back(FSourcifyCoreUEWrapper::Get().Wrap(*DerivedClass));
	}
}

bool SCW_UnrealEngine::IsChildOf_UActorComponent(ISCW_UClassConstPtr InClassPtr) const
{
	return dynamic_cast<const SCW_UClass*>(InClassPtr.get())->ToUClass()->IsChildOf<UActorComponent>();
}
		
bool SCW_UnrealEngine::IsChildOf_UWidget(ISCW_UClassConstPtr InClassPtr) const
{
	return dynamic_cast<const SCW_UClass*>(InClassPtr.get())->ToUClass()->IsChildOf<UWidget>();
}

vector<ISCW_FPropertyPtr> SCW_UnrealEngine::TFieldIterator_GetFProperties(const ISCW_UStructConstPtr Struct, ISCW_EFieldIterationFlags InIterationFlags/* = ISCW_EFieldIterationFlags::Default*/) const
{
	vector<ISCW_FPropertyPtr> Result;
	for (TFieldIterator<FProperty> It(dynamic_cast<const SCW_UStruct*>(Struct.get())->ToUStructPtr(), static_cast<EFieldIterationFlags>(InIterationFlags)); It; ++It)
	{
		Result.push_back(FSourcifyCoreUEWrapper::Get().Wrap(*const_cast<FProperty*>(*It)));
	}
	return Result;
}

vector<ISCW_FObjectPropertyPtr> SCW_UnrealEngine::TFieldRange_FObjectProperties(ISCW_UStructConstPtr InStructPtr, ISCW_EFieldIterationFlags InIterationFlags/* = ISCW_EFieldIterationFlags::Default*/) const
{
	vector<ISCW_FObjectPropertyPtr> Result;
	for (FObjectProperty* ObjectProperty : TFieldRange<FObjectProperty>(dynamic_cast<const SCW_UStruct*>(InStructPtr.get())->ToUStructPtr(), static_cast<EFieldIterationFlags>(InIterationFlags)))
	{
		Result.push_back(FSourcifyCoreUEWrapper::Get().Wrap(*const_cast<FObjectProperty*>(ObjectProperty)));
	}
	return Result;
}

vector<ISCW_UFunctionPtr> SCW_UnrealEngine::TFieldRange_UFunctions(ISCW_UStructConstPtr InStructPtr, ISCW_EFieldIterationFlags InIterationFlags/* = ISCW_EFieldIterationFlags::Default*/) const
{
	vector<ISCW_UFunctionPtr> Result;
	for (UFunction* Function : TFieldRange<UFunction>(dynamic_cast<const SCW_UStruct*>(InStructPtr.get())->ToUStructPtr(), static_cast<EFieldIterationFlags>(InIterationFlags)))
	{
		Result.push_back(FSourcifyCoreUEWrapper::Get().Wrap(*const_cast<UFunction*>(Function)));
	}
	return Result;
}

bool SCW_UnrealEngine::FDesktopPlatformModule_GenerateProjectFiles(const wstring& RootDir, const wstring& ProjectFileName) const
{
	return FDesktopPlatformModule::Get()->GenerateProjectFiles(WCHAR_TO_TCHAR(RootDir.c_str()), WCHAR_TO_TCHAR(ProjectFileName.c_str()), GWarn);
}

bool SCW_UnrealEngine::FAssetRegistryModule_GetRegistry_GetReferencers(const wstring& PackageName, vector<ISCW_UObjectPtr>& OutReferencers) const
{
	TArray<FAssetDependency> Referencers;
	FAssetRegistryModule::GetRegistry().GetReferencers(FAssetIdentifier::FromString(PackageName.c_str()), Referencers, UE::AssetRegistry::EDependencyCategory::All, UE::AssetRegistry::FDependencyQuery(UE::AssetRegistry::EDependencyQuery::NoRequirements));
	for (const FAssetDependency& Referencer : Referencers)
	{
		UObject* Asset = LoadObject<UObject>(nullptr, *Referencer.AssetId.PackageName.ToString());
		if (Asset)
		{
			OutReferencers.push_back(FSourcifyCoreUEWrapper::Get().Wrap(*Asset));
		}
	}

	return OutReferencers.size() > 0;
}

void SCW_UnrealEngine::FAssetRegistryModule_SearchAllAssets(bool bSynchronousSearch) const
{
	return FAssetRegistryModule::GetRegistry().SearchAllAssets(bSynchronousSearch);
}

ISCW_UEdGraphPtr SCW_UnrealEngine::FBlueprintEditorUtils_FindUserConstructionScript(ISCW_UBlueprintPtr Blueprint) const
{
	UEdGraph* Graph = FBlueprintEditorUtils::FindUserConstructionScript(dynamic_cast<SCW_UBlueprint*>(Blueprint.get())->ToUBlueprintRawPtr());
	return Graph ? FSourcifyCoreUEWrapper::Get().Wrap(*Graph) : nullptr;
}

int32_t SCW_UnrealEngine::FBlueprintEditorUtils_GetChildrenOfBlueprint(ISCW_UBlueprintConstPtr Blueprint, vector<ISCW_UBlueprintPtr>& OutChildren) const
{
	TArray<FAssetData> Children;
	FBlueprintEditorUtils::GetChildrenOfBlueprint(dynamic_cast<const SCW_UBlueprint*>(Blueprint.get())->ToUBlueprintRawPtr(), Children);
	for (FAssetData Child : Children)
	{
		if (UBlueprint* ChildBlueprint = Cast<UBlueprint>(Child.GetAsset()))
		{
			OutChildren.push_back(FSourcifyCoreUEWrapper::Get().Wrap(*ChildBlueprint));
		}
	}

	return OutChildren.size();
}

bool SCW_UnrealEngine::FBlueprintEditorUtils_IsDataOnlyBlueprint(ISCW_UBlueprintConstPtr Blueprint) const
{
	return FBlueprintEditorUtils::IsDataOnlyBlueprint(dynamic_cast<const SCW_UBlueprint*>(Blueprint.get())->ToUBlueprintRawPtr());
}

void SCW_UnrealEngine::FBlueprintEditorUtils_ConformCallsToParentFunctions(ISCW_UBlueprintPtr Blueprint) const
{
	FBlueprintEditorUtils::ConformCallsToParentFunctions(dynamic_cast<SCW_UBlueprint*>(Blueprint.get())->ToUBlueprintRawPtr());
}

void SCW_UnrealEngine::FBlueprintEditorUtils_ConformImplementedEvents(ISCW_UBlueprintPtr Blueprint) const
{
	FBlueprintEditorUtils::ConformImplementedEvents(dynamic_cast<SCW_UBlueprint*>(Blueprint.get())->ToUBlueprintRawPtr());
}

void SCW_UnrealEngine::FBlueprintEditorUtils_ConformImplementedInterfaces(ISCW_UBlueprintPtr Blueprint) const
{
	FBlueprintEditorUtils::ConformImplementedInterfaces(dynamic_cast<SCW_UBlueprint*>(Blueprint.get())->ToUBlueprintRawPtr());
}
	
void SCW_UnrealEngine::FBlueprintEditorUtils_MarkBlueprintAsModified(ISCW_UBlueprintPtr Blueprint) const
{
	FBlueprintEditorUtils::MarkBlueprintAsModified(dynamic_cast<SCW_UBlueprint*>(Blueprint.get())->ToUBlueprintRawPtr());
}

void SCW_UnrealEngine::FBlueprintEditorUtils_MarkBlueprintAsStructurallyModified(ISCW_UBlueprintPtr Blueprint) const
{
	FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(dynamic_cast<SCW_UBlueprint*>(Blueprint.get())->ToUBlueprintRawPtr());
}

void SCW_UnrealEngine::FBlueprintEditorUtils_PostEditChangeBlueprintActors(ISCW_UBlueprintPtr Blueprint) const
{
	FBlueprintEditorUtils::PostEditChangeBlueprintActors(dynamic_cast<SCW_UBlueprint*>(Blueprint.get())->ToUBlueprintRawPtr());
}

bool SCW_UnrealEngine::FBlueprintEditorUtils_PropertyValueToString_UObject(ISCW_FPropertyConstPtr Property, ISCW_UObjectPtr Object, wstring& OutString) const
{
	FString OutStringFString;
	bool Result = FBlueprintEditorUtils::PropertyValueToString(dynamic_cast<const SCW_FProperty*>(Property.get())->ToFProperty(), reinterpret_cast<const uint8*>(dynamic_cast<const SCW_UObject*>(Object.get())->ToUObjectRawPtr()), OutStringFString);
	OutString = *OutStringFString;
	return Result;
}

void SCW_UnrealEngine::FBlueprintEditorUtils_RefreshAllNodes(ISCW_UBlueprintPtr Blueprint) const
{
	FBlueprintEditorUtils::RefreshAllNodes(dynamic_cast<SCW_UBlueprint*>(Blueprint.get())->ToUBlueprintRawPtr());
}

void SCW_UnrealEngine::FBlueprintEditorUtils_RemoveInterface(ISCW_UBlueprintPtr Blueprint, const wstring& InterfaceClassPathName, bool bPreserveFunctions /*= false*/) const
{
	FBlueprintEditorUtils::RemoveInterface(dynamic_cast<SCW_UBlueprint*>(Blueprint.get())->ToUBlueprintRawPtr(), FTopLevelAssetPath(InterfaceClassPathName.c_str()), bPreserveFunctions);
}

void SCW_UnrealEngine::FBlueprintEditorUtils_RemoveVariableNodes(ISCW_UBlueprintPtr Blueprint, const wstring& Name) const
{
	FBlueprintEditorUtils::RemoveVariableNodes(dynamic_cast<SCW_UBlueprint*>(Blueprint.get())->ToUBlueprintRawPtr(), FName(Name.c_str()));
}

void SCW_UnrealEngine::FBlueprintEditorUtils_RenameFunctionReferences(ISCW_UBlueprintPtr Blueprint, ISCW_UClassPtr FunctionClass, const wstring& OldFuncName, const wstring& NewFuncName) const
{
	checkNoEntry();
//	FBlueprintEditorUtils::RenameFunctionReferences(dynamic_cast<SCW_UBlueprint*>(Blueprint.get())->ToUBlueprintRawPtr(), dynamic_cast<SCW_UClass*>(FunctionClass.get())->ToUClass(), FName(OldFuncName.c_str()), FName(NewFuncName.c_str()));
}

void SCW_UnrealEngine::FBlueprintEditorUtils_RenameGraph(ISCW_UEdGraphPtr Graph, const wstring& NewName) const
{
	FBlueprintEditorUtils::RenameGraph(dynamic_cast<SCW_UEdGraph*>(Graph.get())->ToUEdGraph(), NewName.c_str());
}

void SCW_UnrealEngine::FBlueprintEditorUtils_UpdateOutOfDateCompositeNodes(ISCW_UBlueprintPtr Blueprint) const
{
	FBlueprintEditorUtils::UpdateOutOfDateCompositeNodes(dynamic_cast<SCW_UBlueprint*>(Blueprint.get())->ToUBlueprintRawPtr());
}

void SCW_UnrealEngine::FBlueprintEditorUtils_UpdateTransactionalFlags(ISCW_UBlueprintPtr Blueprint) const
{
	FBlueprintEditorUtils::UpdateTransactionalFlags(dynamic_cast<SCW_UBlueprint*>(Blueprint.get())->ToUBlueprintRawPtr());
}

wstring SCW_UnrealEngine::FCommandLine_Get() const
{
	return FCommandLine::Get();
}

vector<vector<wstring>> SCW_UnrealEngine::FCsvParser_GetRows(const wstring& CsvContent) const
{
	FCsvParser Parser(FString(CsvContent.c_str()));
	const FCsvParser::FRows& Rows = Parser.GetRows();
	vector<vector<wstring>> Result;
	Result.reserve(Rows.Num());
	for (const TArray<const TCHAR*>& Row : Rows)
	{
		vector<wstring>& ResultRow = Result.emplace_back();
		ResultRow.reserve(Row.Num());
		for (const TCHAR* Cell : Row)
		{
			ResultRow.emplace_back(Cell);
		}
	}
	return Result;
}

bool SCW_UnrealEngine::FFileHelper_SaveStringToFile(wstring String, const wchar_t* Filename) const
{
	FStringView StringView(String.c_str(), String.size());
	return FFileHelper::SaveStringToFile(StringView, Filename);
}

bool SCW_UnrealEngine::FFileHelper_LoadFileToString(wstring& Result, const wchar_t* Filename) const
{
	FString StringResult;
	bool ReturnResult = FFileHelper::LoadFileToString(StringResult, Filename);
	Result = *StringResult;
	return ReturnResult;
}

ISCW_FGraphNodeCreator_UK2Node_EventPtr SCW_UnrealEngine::FGraphNodeCreator_UK2Node_Event(ISCW_UEdGraphPtr Graph) const
{
	return FSourcifyCoreUEWrapper::Get().Wrap(*new FGraphNodeCreator<UK2Node_Event>(*dynamic_cast<SCW_UEdGraph*>(Graph.get())->ToUEdGraph()));
}

ISCW_FGraphNodeCreator_UK2Node_CreateDelegatePtr SCW_UnrealEngine::FGraphNodeCreator_UK2Node_CreateDelegate(ISCW_UEdGraphPtr Graph) const
{
	return FSourcifyCoreUEWrapper::Get().Wrap(*new FGraphNodeCreator<UK2Node_CreateDelegate>(*dynamic_cast<SCW_UEdGraph*>(Graph.get())->ToUEdGraph()));
}

ISCW_FObjectPropertyConstPtr SCW_UnrealEngine::FindFProperty(ISCW_UBlueprintGeneratedClassPtr Owner, const wstring& FieldName) const
{
	SCW_UBlueprintGeneratedClass* SCW_Owner = dynamic_cast<SCW_UBlueprintGeneratedClass*>(Owner.get());
	FObjectProperty* FoundField = ::FindFProperty<FObjectProperty>(SCW_Owner->ToUBlueprintGeneratedClass(), WCHAR_TO_TCHAR(FieldName.c_str()));
	return FoundField ? FSourcifyCoreUEWrapper::Get().Wrap(*FoundField) : nullptr;
}

ISCW_FObjectPropertyConstPtr SCW_UnrealEngine::FindFProperty(ISCW_UClassPtr Owner, const wstring& FieldName) const
{
	FObjectProperty* FoundField = ::FindFProperty<FObjectProperty>(dynamic_cast<SCW_UClass*>(Owner.get())->ToUClass(), WCHAR_TO_TCHAR(FieldName.c_str()));
	return FoundField ? FSourcifyCoreUEWrapper::Get().Wrap(*FoundField) : nullptr;
}

#if ISCW_UE_VERSION_NEWER_THAN_OR_EQUAL(5, 7, 0)
ISCW_UObjectPtr SCW_UnrealEngine::FindObject(ISCW_UObjectPtr Outer, const wstring& Name, SCW_EFindObjectFlags Flags /* = SCW_EFindObjectFlags::None */) const
{
	UObject* OuterUObject = Outer ? dynamic_cast<SCW_UObject*>(Outer.get())->ToUObjectRawPtr() : nullptr;
	UObject* FoundObject = ::FindObject<UObject>(OuterUObject, Name.c_str(), static_cast<EFindObjectFlags>(Flags));
	return FoundObject ? FSourcifyCoreUEWrapper::Get().Wrap(*FoundObject) : nullptr;
}
#else
ISCW_UObjectPtr SCW_UnrealEngine::FindObject(ISCW_UObjectPtr Outer, const wstring& Name, bool bExactClass /* = false */) const
{
	UObject* OuterUObject = dynamic_cast<const SCW_UObject*>(Outer.get())->ToUObjectRawPtr();
	UObject* FoundObject = ::FindObject<UObject>(OuterUObject, Name.c_str(), bExactClass);
	return FoundObject ? FSourcifyCoreUEWrapper::Get().Wrap(*FoundObject) : nullptr;
}
#endif

#if ISCW_UE_VERSION_NEWER_THAN_OR_EQUAL(5, 7, 0)
ISCW_UEnumPtr SCW_UnrealEngine::FindObject_UEnum(ISCW_UObjectPtr Outer, const wstring& Name, SCW_EFindObjectFlags Flags /* = SCW_EFindObjectFlags::None */) const
{
	UObject* OuterUObject = Outer ? dynamic_cast<SCW_UObject*>(Outer.get())->ToUObjectRawPtr() : nullptr;
	UEnum* FoundObject = ::FindObject<UEnum>(OuterUObject, Name.c_str(), static_cast<EFindObjectFlags>(Flags));
	if (FoundObject)
	{
		return FSourcifyCoreUEWrapper::Get().Wrap(*FoundObject);
	}

	return nullptr;
}
#else
ISCW_UEnumPtr SCW_UnrealEngine::FindObject_UEnum(ISCW_UObjectPtr Outer, const wstring& Name, bool bExactClass /* = false */) const
{
	UObject* OuterUObject = dynamic_cast<const SCW_UObject*>(Outer.get())->ToUObjectRawPtr();
	UEnum* FoundObject = ::FindObject<UEnum>(OuterUObject, Name.c_str(), bExactClass);
	if (FoundObject)
	{
		return FSourcifyCoreUEWrapper::Get().Wrap(*FoundObject);
	}
	return nullptr;
}
#endif

bool SCW_UnrealEngine::FKismetEditorUtilities_CompileBlueprint(ISCW_UBlueprintPtr Blueprint, SCW_EBlueprintCompileOptions Options) const
{
	FCompilerResultsLog CompilerResults;
	FKismetEditorUtilities::CompileBlueprint(dynamic_cast<SCW_UBlueprint*>(Blueprint.get())->ToUBlueprintRawPtr(), static_cast<EBlueprintCompileOptions>(Options), &CompilerResults);
	return CompilerResults.NumErrors == 0;
}

void SCW_UnrealEngine::FKismetEditorUtilities_UpgradeCosmeticallyStaleBlueprint(ISCW_UBlueprintPtr Blueprint) const
{
	FKismetEditorUtilities::UpgradeCosmeticallyStaleBlueprint(dynamic_cast<SCW_UBlueprint*>(Blueprint.get())->ToUBlueprintRawPtr());
}

wstring SCW_UnrealEngine::FName_NameToDisplayString(const wstring& InName, bool bIsBool/* = false*/) const
{
	return *FName::NameToDisplayString(InName.c_str(), bIsBool);
}

void SCW_UnrealEngine::FPlatformMisc_RequestExit(bool Force) const
{
	FPlatformMisc::RequestExit(Force);
}

bool SCW_UnrealEngine::GConfig_IsKnownConfigName(const wstring& ConfigName) const
{
	return GConfig->IsKnownConfigName(ConfigName.c_str());
}

bool SCW_UnrealEngine::ILiveCoding_IsEnabledForSession() const
{
	ILiveCodingModule* LiveCoding = FModuleManager::GetModulePtr<ILiveCodingModule>(LIVE_CODING_MODULE_NAME);
	return LiveCoding != nullptr && LiveCoding->IsEnabledForSession();
}

bool SCW_UnrealEngine::ILiveCoding_Compile() const
{
	ILiveCodingModule* LiveCoding = FModuleManager::GetModulePtr<ILiveCodingModule>(LIVE_CODING_MODULE_NAME);
	ELiveCodingCompileResult CompileResults;
	LiveCoding->Compile(ELiveCodingCompileFlags::WaitForCompletion, &CompileResults);
	return CompileResults == ELiveCodingCompileResult::Success;
}
	
void SCW_UnrealEngine::ISourceControlProvider_NotifySourceControl(const vector<wstring>& InFiles) const
{
	ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
	if (ISourceControlModule::Get().IsEnabled() && SourceControlProvider.IsAvailable())
	{
		TArray<FString> Files;
		for (const wstring& File : InFiles)
		{
			Files.Add(WCHAR_TO_TCHAR(File.c_str()));
		}

		// Add new files
		SourceControlProvider.Execute(ISourceControlOperation::Create<FMarkForAdd>(), Files);
		// Check out any existing files
		SourceControlProvider.Execute(ISourceControlOperation::Create<FCheckOut>(), Files);
	}
}

void* SCW_UnrealEngine::FMemory_Malloc(size_t Size, size_t Alignment/* = 0*/) const
{
	return FMemory::Malloc(Size, Alignment);
}

void SCW_UnrealEngine::FMemory_Free(void* Original) const
{
	FMemory::Free(Original);
}

void* SCW_UnrealEngine::FMemory_Realloc(void* Original, size_t Size, size_t Alignment/* = 0*/) const
{
	return FMemory::Realloc(Original, Size, Alignment);
}

ISCW_FSlowTaskPtr SCW_UnrealEngine::FSlowTask_New(int32_t InAmountOfWork, const wstring& InDefaultMessage /* = L"" */, bool bInEnabled /* = true */) const
{
	return FSourcifyCoreUEWrapper::Get().Wrap(*new FSlowTask(InAmountOfWork, FText::FromString(InDefaultMessage.c_str()), bInEnabled));
}

bool SCW_UnrealEngine::FSourceCodeNavigation_FindModulePath(const wstring& ModuleName, wstring& OutPath)
{
	FString OutPathFString;
	if (FSourceCodeNavigation::FindModulePath(WCHAR_TO_TCHAR(ModuleName.c_str()), OutPathFString))
	{
		OutPath = *OutPathFString;
		return true;
	}

	return false;
}

wstring SCW_UnrealEngine::FTopLevelAssetPath_ToString(ISCW_UFieldConstPtr Field)
{
	return *FTopLevelAssetPath(dynamic_cast<const SCW_UField*>(Field.get())->ToUObjectRawPtr()).ToString();
}

wstring SCW_UnrealEngine::FModuleManager_GetModuleFilename(const wstring& ModuleName) const
{
	return *FModuleManager::Get().GetModuleFilename(WCHAR_TO_TCHAR(ModuleName.c_str()));
}

vector<ISCW_UClassPtr> SCW_UnrealEngine::TObjectIterator_GetClasses(SCW_EObjectFlags AdditionalExclusionFlags/* = SCW_EObjectFlags::RF_ClassDefaultObject*/, bool bIncludeDerivedClasses/* = true*/) const
{
	vector<ISCW_UClassPtr> Result;
	for (TObjectIterator<UClass> It(static_cast<EObjectFlags>(AdditionalExclusionFlags), bIncludeDerivedClasses); It; ++It)
	{
		Result.push_back(FSourcifyCoreUEWrapper::Get().Wrap(*(*It)));
	}
	return Result;
}

vector<ISCW_UEnumPtr> SCW_UnrealEngine::TObjectIterator_GetEnums(SCW_EObjectFlags AdditionalExclusionFlags/* = SCW_EObjectFlags::RF_ClassDefaultObject*/, bool bIncludeDerivedClasses/* = true*/) const
{
	vector<ISCW_UEnumPtr> Result;
	for (TObjectIterator<UEnum> It(static_cast<EObjectFlags>(AdditionalExclusionFlags), bIncludeDerivedClasses); It; ++It)
	{
		Result.push_back(FSourcifyCoreUEWrapper::Get().Wrap(*(*It)));
	}
	return Result;
}

vector<ISCW_UObjectPtr> SCW_UnrealEngine::TObjectIterator_GetObjects(SCW_EObjectFlags AdditionalExclusionFlags /* = SCW_EObjectFlags::RF_ClassDefaultObject */, bool bIncludeDerivedClasses /* = true */) const
{
	vector<ISCW_UObjectPtr> Result;
	for (TObjectIterator<UObject> It(static_cast<EObjectFlags>(AdditionalExclusionFlags), bIncludeDerivedClasses); It; ++It)
	{
		Result.push_back(FSourcifyCoreUEWrapper::Get().Wrap(*(*It)));
	}
	return Result;
}

vector<ISCW_UStructPtr> SCW_UnrealEngine::TObjectIterator_GetStructs(SCW_EObjectFlags AdditionalExclusionFlags/* = SCW_EObjectFlags::RF_ClassDefaultObject*/, bool bIncludeDerivedClasses/* = true*/) const
{
	vector<ISCW_UStructPtr> Result;
	for (TObjectIterator<UStruct> It(static_cast<EObjectFlags>(AdditionalExclusionFlags), bIncludeDerivedClasses); It; ++It)
	{
		Result.push_back(FSourcifyCoreUEWrapper::Get().Wrap(*(*It)));
	}
	return Result;
}

wstring SCW_UnrealEngine::FPaths_ConvertRelativePathToFull(const wstring& InPath) const
{
	return *FPaths::ConvertRelativePathToFull(WCHAR_TO_TCHAR(InPath.c_str()));
}

wstring SCW_UnrealEngine::FPaths_GameSourceDir() const
{
	return *FPaths::GameSourceDir();
}

bool SCW_UnrealEngine::FPaths_FileExists(const wstring& InPath)
{
	return FPaths::FileExists(WCHAR_TO_TCHAR(InPath.c_str()));
}

wstring SCW_UnrealEngine::FPaths_GetBaseFilename(const wstring& InPath, bool bRemovePath/* = true*/) const
{
	return *FPaths::GetBaseFilename(WCHAR_TO_TCHAR(InPath.c_str()), bRemovePath);
}

wstring SCW_UnrealEngine::FPaths_GetExtension(const wstring& InPath) const
{
	return *FPaths::GetExtension(WCHAR_TO_TCHAR(InPath.c_str()));
}

wstring SCW_UnrealEngine::FPaths_GetPath(const wstring& InPath) const
{
	return *FPaths::GetPath(WCHAR_TO_TCHAR(InPath.c_str()));
}

wstring SCW_UnrealEngine::FPaths_GetProjectFilePath() const
{
	return *FPaths::GetProjectFilePath();
}

wstring SCW_UnrealEngine::FPaths_ProjectSavedDir() const
{
	return *FPaths::ProjectSavedDir();
}

bool SCW_UnrealEngine::FPaths_MakePathRelativeTo(wstring& InOutPath, const wstring& InRelativeTo) const
{
	FString Path = InOutPath.c_str();
	bool Result = FPaths::MakePathRelativeTo(Path, WCHAR_TO_TCHAR(InRelativeTo.c_str()));
	InOutPath = *Path;

	return Result;
}

void SCW_UnrealEngine::FPaths_NormalizeDirectoryName(wstring& InOutPath) const
{
	FString Path = InOutPath.c_str();
	FPaths::NormalizeDirectoryName(Path);
	InOutPath = *Path;
}

void SCW_UnrealEngine::FPaths_NormalizeFilename(wstring& InOutPath) const
{
	FString Path = InOutPath.c_str();
	FPaths::NormalizeFilename(Path);
	InOutPath = *Path;
}

wstring SCW_UnrealEngine::FPaths_RootDir() const
{
	return *FPaths::RootDir();
}

bool SCW_UnrealEngine::FPlatformFileManager_GetPlatformFile_DirectoryExists(const wstring& InPath) const
{
	return FPlatformFileManager::Get().GetPlatformFile().DirectoryExists(WCHAR_TO_TCHAR(InPath.c_str()));
}

bool SCW_UnrealEngine::FPlatformFileManager_GetPlatformFile_FileExists(const wstring& InFile) const
{
	return FPlatformFileManager::Get().GetPlatformFile().FileExists(WCHAR_TO_TCHAR(InFile.c_str()));
}

bool SCW_UnrealEngine::FPlatformFileManager_GetPlatformFile_IterateDirectory(const wstring& InPath, std::function<bool(const wchar_t*, bool)> Visitor) const
{
	return FPlatformFileManager::Get().GetPlatformFile().IterateDirectory(WCHAR_TO_TCHAR(InPath.c_str()), Visitor);
}

bool SCW_UnrealEngine::FPlatformFileManager_GetPlatformFile_IterateDirectoryRecursively(const wstring& InPath, std::function<bool(const wchar_t*, bool)> Visitor) const
{
	return FPlatformFileManager::Get().GetPlatformFile().IterateDirectoryRecursively(WCHAR_TO_TCHAR(InPath.c_str()), Visitor);
}

bool SCW_UnrealEngine::FPlatformFileManager_GetPlatformFile_SetReadOnly(const wstring& InFile, bool bNewReadOnlyValue) const
{
	return FPlatformFileManager::Get().GetPlatformFile().SetReadOnly(WCHAR_TO_TCHAR(InFile.c_str()), bNewReadOnlyValue);
}

ISCW_UPackagePtr SCW_UnrealEngine::GetTransientPackage() const
{
	return FSourcifyCoreUEWrapper::Get().Wrap(*::GetTransientPackage());
}

bool SCW_UnrealEngine::IsValid(ISCW_UObjectConstPtr Test) const
{
	return Test && ::IsValid(dynamic_cast<const SCW_UObject*>(Test.get())->ToUObjectRawPtr());
}

void SCW_UnrealEngine::LogMacros_UE_LOG(SCW_UE_LOG_Category Category, SCW_ELogVerbosity Verbosity, const wstring& Message, const wstring Filename/* = L""*/, const int32_t LineNumber/*=-1*/) const
{
	wstring FullMessage = Message;
	
	if (!Filename.empty())
	{
		FullMessage += L" at " + Filename;
		if (LineNumber > -1)
		{
			FullMessage += L":" + std::to_wstring(LineNumber);
		}
	}

	switch (Category)
	{
	case SCW_UE_LOG_Category::LogSourcifyBPPostProcess:
		switch (Verbosity)
		{
		case SCW_ELogVerbosity::Fatal: UE_LOG(LogSourcifyBPPostProcess, Fatal, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::Error: UE_LOG(LogSourcifyBPPostProcess, Error, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::Warning: UE_LOG(LogSourcifyBPPostProcess, Warning, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		default: case SCW_ELogVerbosity::Display: UE_LOG(LogSourcifyBPPostProcess, Display, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::Log: UE_LOG(LogSourcifyBPPostProcess, Log, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::Verbose: UE_LOG(LogSourcifyBPPostProcess, Verbose, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::VeryVerbose: UE_LOG(LogSourcifyBPPostProcess, VeryVerbose, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		}
		break;
	case SCW_UE_LOG_Category::LogSourcifyCoreModule:
		switch (Verbosity)
		{
		case SCW_ELogVerbosity::Fatal: UE_LOG(LogSourcifyCoreModule, Fatal, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::Error: UE_LOG(LogSourcifyCoreModule, Error, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::Warning: UE_LOG(LogSourcifyCoreModule, Warning, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		default: case SCW_ELogVerbosity::Display: UE_LOG(LogSourcifyCoreModule, Display, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::Log: UE_LOG(LogSourcifyCoreModule, Log, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::Verbose: UE_LOG(LogSourcifyCoreModule, Verbose, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::VeryVerbose: UE_LOG(LogSourcifyCoreModule, VeryVerbose, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		}
		break;
	case SCW_UE_LOG_Category::LogSourcifyLicenseManager:
		switch (Verbosity)
		{
		case SCW_ELogVerbosity::Fatal: UE_LOG(LogSourcifyLicenseManager, Fatal, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::Error: UE_LOG(LogSourcifyLicenseManager, Error, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::Warning: UE_LOG(LogSourcifyLicenseManager, Warning, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		default: case SCW_ELogVerbosity::Display: UE_LOG(LogSourcifyLicenseManager, Display, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::Log: UE_LOG(LogSourcifyLicenseManager, Log, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::Verbose: UE_LOG(LogSourcifyLicenseManager, Verbose, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::VeryVerbose: UE_LOG(LogSourcifyLicenseManager, VeryVerbose, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		}
		break;
	case SCW_UE_LOG_Category::LogSourcifyCoreModuleUtilities:
		switch (Verbosity)
		{
		case SCW_ELogVerbosity::Fatal: UE_LOG(LogSourcifyCoreModuleUtilities, Fatal, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::Error: UE_LOG(LogSourcifyCoreModuleUtilities, Error, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::Warning: UE_LOG(LogSourcifyCoreModuleUtilities, Warning, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		default: case SCW_ELogVerbosity::Display: UE_LOG(LogSourcifyCoreModuleUtilities, Display, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::Log: UE_LOG(LogSourcifyCoreModuleUtilities, Log, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::Verbose: UE_LOG(LogSourcifyCoreModuleUtilities, Verbose, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::VeryVerbose: UE_LOG(LogSourcifyCoreModuleUtilities, VeryVerbose, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		}
		break;
	default:
	case SCW_UE_LOG_Category::LogSourcifyCore:
		switch (Verbosity)
		{
		case SCW_ELogVerbosity::Fatal: UE_LOG(LogSourcifyCore, Fatal, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::Error: UE_LOG(LogSourcifyCore, Error, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::Warning: UE_LOG(LogSourcifyCore, Warning, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		default: case SCW_ELogVerbosity::Display: UE_LOG(LogSourcifyCore, Display, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::Log: UE_LOG(LogSourcifyCore, Log, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::Verbose: UE_LOG(LogSourcifyCore, Verbose, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		case SCW_ELogVerbosity::VeryVerbose: UE_LOG(LogSourcifyCore, VeryVerbose, TEXT("%s"), WCHAR_TO_TCHAR(FullMessage.c_str())); break;
		}
		break;
	}
}

ISCW_UBlueprintPtr SCW_UnrealEngine::LoadObject_UBlueprint(ISCW_UObjectPtr Outer, const wstring& Path) const
{
	UObject* OuterUObject = nullptr;
	if (Outer)
	{
		OuterUObject = dynamic_cast<SCW_UObject*>(Outer.get())->ToUObjectRawPtr();
	}

	UBlueprint* LoadedObject = LoadObject<UBlueprint>(OuterUObject, Path.c_str());
	if (LoadedObject)
	{
		return FSourcifyCoreUEWrapper::Get().Wrap(*LoadedObject);
	}

	return nullptr;
}

ISCW_UObjectPtr SCW_UnrealEngine::LoadObject_UObject(ISCW_UObjectPtr Outer, const wstring& Path) const
{
	UObject* OuterUObject = nullptr;
	if (Outer)
	{
		OuterUObject = dynamic_cast<SCW_UObject*>(Outer.get())->ToUObjectRawPtr();
	}

	UObject* LoadedObject = LoadObject<UObject>(OuterUObject, Path.c_str());
	if (LoadedObject)
	{
		return FSourcifyCoreUEWrapper::Get().Wrap(*LoadedObject);
	}

	return nullptr;
}

wstring SCW_UnrealEngine::MakeUniqueObjectName(ISCW_UObjectPtr Outer, ISCW_UClassPtr Class, const wstring& BaseName) const
{
	return *::MakeUniqueObjectName(dynamic_cast<SCW_UObject*>(Outer.get())->ToUObjectRawPtr(), dynamic_cast<SCW_UClass*>(Class.get())->ToUClass(), FName{ BaseName.c_str() }).ToString();
}

ISCW_UObjectPtr SCW_UnrealEngine::NewObject_UObject(ISCW_UObjectPtr Outer, ISCW_UClassPtr Class, const wstring& Name, SCW_EObjectFlags Flags/* = SCW_EObjectFlags::RF_NoFlags*/) const
{
	UObject* NewObj = NewObject<UObject>(dynamic_cast<SCW_UObject*>(Outer.get())->ToUObjectRawPtr(), dynamic_cast<SCW_UClass*>(Class.get())->ToUClass(), Name.empty() ? NAME_None : FName{ Name.c_str() }, static_cast<EObjectFlags>(Flags));
	if (NewObj)
	{
		return FSourcifyCoreUEWrapper::Get().Wrap(*NewObj);
	}
	return nullptr;
}

ISCW_USimpleConstructionScriptPtr SCW_UnrealEngine::NewObject_USimpleConstructionScript(ISCW_UBlueprintGeneratedClassPtr Class, const wstring& Name/* = L""*/) const 
{
	USimpleConstructionScript* NewObj = NewObject<USimpleConstructionScript>(dynamic_cast<SCW_UBlueprintGeneratedClass*>(Class.get())->ToUBlueprintGeneratedClass(), Name.empty() ? NAME_None : FName{ Name.c_str() });
	if (NewObj)
	{
		return FSourcifyCoreUEWrapper::Get().Wrap(*NewObj);
	}
	return nullptr;
}

void SCW_UnrealEngine::ObjectTools_ConsolidateObjects(ISCW_UObjectPtr ObjectToConsolidateTo, const vector<ISCW_UObjectPtr>& ObjectsToConsolidate, bool bShowDeleteConfirmation /* = true */) const
{
	TArray<UObject*> ObjectsToConsolidateUE;
	for (ISCW_UObjectPtr Object : ObjectsToConsolidate)
	{
		ObjectsToConsolidateUE.Add(dynamic_cast<SCW_UObject*>(Object.get())->ToUObjectRawPtr());
	}
	ObjectTools::ConsolidateObjects(dynamic_cast<SCW_UObject*>(ObjectToConsolidateTo.get())->ToUObjectRawPtr(), ObjectsToConsolidateUE, bShowDeleteConfirmation);
}

bool SCW_UnrealEngine::GameProjectUtils_IsValidClassNameForCreation(const wstring& ClassName, const wstring& OutputDirectory, wstring& OutReason) const
{
	const TSet<FString>& DisallowedHeaderNames = FSourceCodeNavigation::GetSourceFileDatabase().GetDisallowedHeaderNames();
	FModuleContextInfo SelectedModuleInfo;
	FString OutputDirectoryFString = OutputDirectory.c_str();
	auto IndexOfSource = OutputDirectoryFString.Find(TEXT("/Source"));
	if (IndexOfSource != SCW_INDEX_NONE)
	{
		SelectedModuleInfo.ModuleSourcePath = OutputDirectoryFString.Left(IndexOfSource + 8);
	}

	FText Reason;
	bool Result = GameProjectUtils::IsValidClassNameForCreation(ClassName.c_str(), SelectedModuleInfo, DisallowedHeaderNames, Reason);
	OutReason = *Reason.ToString();
	return Result;
}

bool SCW_UnrealEngine::UEditorPerProjectUserSettings_GetAutomaticallyHotReloadNewClasses() const
{
	return GetDefault<UEditorPerProjectUserSettings>()->bAutomaticallyHotReloadNewClasses;
}

void SCW_UnrealEngine::UEngine_CopyPropertiesForUnrelatedObjects(ISCW_UObjectPtr SourceObject, ISCW_UObjectPtr DestinationObject) const
{
	UEngine::FCopyPropertiesForUnrelatedObjectsParams CopyOptions;
	CopyOptions.bNotifyObjectReplacement = true;
	UEngine::CopyPropertiesForUnrelatedObjects(dynamic_cast<SCW_UObject*>(SourceObject.get())->ToUObjectRawPtr(), dynamic_cast<SCW_UObject*>(DestinationObject.get())->ToUObjectRawPtr(), CopyOptions);
}

wstring SCW_UnrealEngine::UGeneralProjectSettings_GetProjectName() const
{
	return *GetDefault<UGeneralProjectSettings>()->ProjectName;
}

// UMetaData is deprecated as of 5.6, use FMetaData instead.
#if ISCW_UE_VERSION_OLDER_THAN(5, 6, 0)

map<wstring, wstring> SCW_UnrealEngine::UMetaData_GetMapForObject(ISCW_UObjectConstPtr Object) const
{
	map<wstring, wstring> Result;
	const SCW_UObject* SCWObject = dynamic_cast<const SCW_UObject*>(Object.get());
	if (TMap<FName, FString>* MetaDataMap = UMetaData::GetMapForObject(SCWObject->ToUObjectRawPtr()))
	{
		for (const auto& Pair : *MetaDataMap)
		{
			Result.emplace(*Pair.Key.ToString(), *Pair.Value);
		}
	}

	return Result;
}

wstring SCW_UnrealEngine::UMetaData_GetRemappedKeyName(const wstring& Key) const
{
	return *UMetaData::GetRemappedKeyName(FName{ Key.c_str() }).ToString();
}

#else

map<wstring, wstring> SCW_UnrealEngine::FMetaData_GetMapForObject(ISCW_UObjectConstPtr Object) const
{
	map<wstring, wstring> Result;
	const SCW_UObject* SCWObject = dynamic_cast<const SCW_UObject*>(Object.get());
	if (const TMap<FName, FString>* MetaDataMap = FMetaData::GetMapForObject(SCWObject->ToUObjectRawPtr()))
	{
		for (const auto& Pair : *MetaDataMap)
		{
			Result.emplace(*Pair.Key.ToString(), *Pair.Value);
		}
	}
	return Result;
}

wstring SCW_UnrealEngine::FMetaData_GetRemappedKeyName(const wstring& Key) const
{
	return *FMetaData::GetRemappedKeyName(FName{ Key.c_str() }).ToString();
}

#endif // ISCW_UE_VERSION_OLDER_THAN(5, 6, 0)

bool SCW_UnrealEngine::UPackageTools_ReloadPackages(vector<ISCW_UPackagePtr> Packages, wstring& ErrorMessage, SCW_EReloadPackagesInteractionMode Mode) const
{
	FText UE_ErrorMessage;
	TArray<UPackage*> UE_Packages;
	for (ISCW_UPackagePtr Package : Packages)
	{
		UE_Packages.Add(dynamic_cast<SCW_UPackage*>(Package.get())->ToUPackage());
	}
		
	bool Result = UPackageTools::ReloadPackages(UE_Packages, UE_ErrorMessage, static_cast<EReloadPackagesInteractionMode>(Mode));
	ErrorMessage = *UE_ErrorMessage.ToString();

	return Result;
}

void SCW_UnrealEngine::USubobjectDataSubsystem_ChangeSubobjectClass(ISCW_FSubobjectDataHandlePtr SubobjectDataHandle, ISCW_UClassPtr NewClass) const
{
	USubobjectDataSubsystem::Get()->ChangeSubobjectClass(dynamic_cast<SCW_FSubobjectDataHandle*>(SubobjectDataHandle.get())->ToFSubobjectDataHandle(), dynamic_cast<SCW_UClass*>(NewClass.get())->ToUClass());
}

void SCW_UnrealEngine::USubobjectDataSubsystem_GatherSubobjectData(ISCW_UObjectPtr Context, vector<ISCW_FSubobjectDataHandlePtr>& OutArray) const
{
	TArray<FSubobjectDataHandle> SubobjectData;
	USubobjectDataSubsystem::Get()->GatherSubobjectData(dynamic_cast<SCW_UObject*>(Context.get())->ToUObjectRawPtr(), SubobjectData);
	for (FSubobjectDataHandle& Handle : SubobjectData)
	{
		OutArray.push_back(FSourcifyCoreUEWrapper::Get().Wrap(Handle));
	}
}

ISCW_UWorldPtr SCW_UnrealEngine::UWorld_CreateWorld(const SCW_EWorldType InWorldType, bool bInformEngineOfWorld, wstring WorldName/* = L"none"*/, ISCW_UPackagePtr InWorldPackage/* = nullptr*/, bool bAddToRoot/* = true*/, SCW_ERHIFeatureLevel InFeatureLevel/* = SCW_ERHIFeatureLevel::Num*//*, const InitializationValues* InIVS = nullptr, bool bInSkipInitWorld = false*/) const
{
	UPackage* WorldPackage = InWorldPackage ? dynamic_cast<SCW_UPackage*>(InWorldPackage.get())->ToUPackage() : nullptr;
	ERHIFeatureLevel::Type FeatureLevel = static_cast<ERHIFeatureLevel::Type>(InFeatureLevel);
	
	return FSourcifyCoreUEWrapper::Get().Wrap(*UWorld::CreateWorld(static_cast<EWorldType::Type>(InWorldType), bInformEngineOfWorld, WorldName.c_str(), WorldPackage, bAddToRoot, FeatureLevel));
}

ISCW_UClassPtr SCW_UnrealEngine::UWorld_StaticClass() const
{
	return FSourcifyCoreUEWrapper::Get().Wrap(*UWorld::StaticClass());
}

shared_ptr<ISCW_UnrealEngine> SCW_UnrealEngine::ToUnrealEnginePtr()
{
	return GetSharedThisAs<SCW_UnrealEngine>();
}

shared_ptr<const ISCW_UnrealEngine> SCW_UnrealEngine::ToUnrealEnginePtr() const
{
	return GetSharedThisAs<SCW_UnrealEngine>();
}

bool SCW_UnrealEngine::GLOBAL_GetGFlushRenderingCommandsOnPreEditChange() const
{
	return GFlushRenderingCommandsOnPreEditChange;
}

void SCW_UnrealEngine::GLOBAL_SetGFlushRenderingCommandsOnPreEditChange(bool bInFlushRenderingCommandsOnPreEditChange) const
{
	GFlushRenderingCommandsOnPreEditChange = bInFlushRenderingCommandsOnPreEditChange;
}

namespace SCW_UnrealEngineHelpers
{
	/**
	 * Singleton to only create error title text if needed (and after localization system is in place)
	 */
	const wstring& GetDefaultMessageTitle()
	{
		// Will be initialised on first call
		static constexpr const wstring DefaultMessageTitle = L"Message";
		return DefaultMessageTitle;
	}
}

void SCW_UnrealEngine::GEditor_CloseAndReopenAssetEditor(ISCW_UObjectPtr Asset)
{
	UObject* UEAsset = dynamic_cast<SCW_UObject*>(Asset.get())->ToUObjectRawPtr();
	UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
	if (!AssetEditorSubsystem)
	{
		return;
	}
	IAssetEditorInstance* EditorInstance = AssetEditorSubsystem->FindEditorForAsset(UEAsset, false);
	if (EditorInstance)
	{
		AssetEditorSubsystem->CloseAllEditorsForAsset(UEAsset);
		AssetEditorSubsystem->OpenEditorForAsset(UEAsset);
	}
}

SCW_EAppReturnType SCW_UnrealEngine::FMessageDialog_Open(SCW_EAppMsgType MessageType, const wstring& Message)
{
	return FMessageDialog_Open(SCW_EAppMsgCategory::Warning, MessageType, Message, SCW_UnrealEngineHelpers::GetDefaultMessageTitle());
}

SCW_EAppReturnType SCW_UnrealEngine::FMessageDialog_Open(SCW_EAppMsgType MessageType, const wstring& Message, const wstring& Title)
{
	return FMessageDialog_Open(SCW_EAppMsgCategory::Warning, MessageType, Message, Title);
}

SCW_EAppReturnType SCW_UnrealEngine::FMessageDialog_Open(SCW_EAppMsgCategory MessageCategory, SCW_EAppMsgType MessageType, const wstring& Message)
{
	return FMessageDialog_Open(MessageCategory, MessageType, Message, SCW_UnrealEngineHelpers::GetDefaultMessageTitle());
}

SCW_EAppReturnType SCW_UnrealEngine::FMessageDialog_Open(SCW_EAppMsgCategory MessageCategory, SCW_EAppMsgType MessageType, const wstring& Message, const wstring& Title)
{
	return static_cast<SCW_EAppReturnType>(
		FMessageDialog::Open(static_cast<EAppMsgCategory>(MessageCategory), static_cast<EAppMsgType::Type>(MessageType),
			FText::FromString(Message.c_str()), FText::FromString(Title.c_str()))
		);
}