#include "PortaStructs.h"

FPortaPropertyDesc::FPortaPropertyDesc(const FProperty* Property)
{
	Name = Property->GetName();

	const FProperty* ValueProperty = Property;
	if (const FArrayProperty* ArrayProperty = CastField<FArrayProperty>(Property))
	{
		ContainerType = Property->GetCPPType();
		ValueProperty = ArrayProperty->Inner;
	}
	else if (const FSetProperty* SetProperty = CastField<FSetProperty>(Property))
	{
		ContainerType = Property->GetCPPType();
		ValueProperty = SetProperty->ElementProp;
	}
	else if (const FMapProperty* MapProperty = CastField<FMapProperty>(Property))
	{
		ContainerType = Property->GetCPPType();
		KeyType = MapProperty->KeyProp->GetCPPType();
		ValueProperty = MapProperty->ValueProp;
	}
	else if (Property->ArrayDim > 1)
	{
		ContainerType = TEXT("CArray");
	}

	//Write the type name
	Type = ValueProperty->GetCPPType();


	//Fill Enum choices metadata
	UEnum* Enum = nullptr;

	if (const FEnumProperty* EnumProperty = CastField<FEnumProperty>(ValueProperty))
	{
		Enum = EnumProperty->GetEnum();
	}

	if (const FByteProperty* ByteProperty = CastField<FByteProperty>(ValueProperty))
	{
		Enum = ByteProperty->Enum;
	}

	if (Enum)
	{
		const int32 EnumCount = Enum->NumEnums() - 1; //Don't list the _MAX entry
		TStringBuilder<256> Builder;
		for (int32 Index = 0; Index < EnumCount; ++Index)
		{
			FString Text = Enum->GetDisplayNameTextByIndex(Index).ToString();
			if (Text.IsEmpty())
			{
				Text = Enum->GetNameStringByIndex(Index);
			}

			if (Index > 0)
			{
				Builder.Append(TEXT(", "));
			}
			Builder.Append(Text);
		}

		if (Builder.Len() > 0)
		{
			Metadata.FindOrAdd(TEXT("EnumValues")) = Builder.ToString();
		}
	}
}


FPortaFunctionDesc::FPortaFunctionDesc(const UFunction* Function)
	: Name(Function->GetName())
{
	FString ComboSource;
#if WITH_EDITOR
	Description = Function->GetMetaData("ToolTip");
	ComboSource = Function->GetMetaData("ComboSource");
#endif

	for (TFieldIterator<FProperty> It(Function); It; ++It)
	{
		if (It && It->HasAnyPropertyFlags(CPF_Parm) && !It->HasAnyPropertyFlags(CPF_ReturnParm | CPF_OutParm))
		{
			Arguments.Emplace(*It);
		}
	}

	//fix up with any custom metadata
	if (Function->GetOwnerClass()->GetName() == TEXT("PortaSequenceFSMActor") && Name == TEXT("GotoState"))
	{
		Arguments[0].Metadata.Add(FName("ComboSource"), TEXT("GetStates"));
	}

	TArray<FString> Combos;
	ComboSource.ParseIntoArray(Combos, TEXT(";"));

	for (auto Combo : Combos)
	{
		FString IndexStr;
		FString ComboFunc;
		if (Combo.Split(TEXT(":"), &IndexStr, &ComboFunc))
		{
			int Index = FCString::Atoi(*IndexStr);
			if (Index >= 0 && Index < Arguments.Num())
			{
				Arguments[Index].Metadata.Add(FName("ComboSource"), ComboFunc);
			}
		}
	}
}

FPortaPresetPropertyDesc::FPortaPresetPropertyDesc(const FRemoteControlProperty& RCProperty) :
	DisplayName(RCProperty.GetLabel()),
	UnderlyingProperty(RCProperty.GetProperty())
{
	ID = RCProperty.GetId().ToString();

#if WITH_EDITOR
	auto ComboSource = RCProperty.GetProperty()->FindMetaData(FName("ComboSource"));
	if (ComboSource)
	{
		UnderlyingProperty.Metadata.Add(FName("ComboSource"), *ComboSource);
	}
#endif
}

FPortaPresetFunctionDesc::FPortaPresetFunctionDesc(const FRemoteControlFunction& Function)
	: DisplayName(Function.GetLabel())
	, ID(Function.GetId().ToString())
	, UnderlyingFunction(Function.GetFunction())
{
	OwnerObjects.Append(Function.GetBoundObjects());
}

FPortaPresetGroupDesc::FPortaPresetGroupDesc(const URemoteControlPreset* Preset, const FRemoteControlPresetGroup& Group) : Name(Group.Name)
{
	for (const FGuid& EntityId : Group.GetFields())
	{
		AddExposedField(Preset, EntityId);
	}
}

void FPortaPresetGroupDesc::AddExposedField(const URemoteControlPreset* Preset, const FGuid& EntityId)
{
	if (TSharedPtr<const FRemoteControlProperty> RCProperty = Preset->GetExposedEntity<FRemoteControlProperty>(EntityId).Pin())
	{
		if (FProperty* Property = RCProperty->GetProperty())
		{
			ExposedProperties.Emplace(*RCProperty);
		}
	}
	else if (TSharedPtr<const FRemoteControlFunction> RCFunction = Preset->GetExposedEntity<FRemoteControlFunction>(EntityId).Pin())
	{
		if (RCFunction->GetFunction())
		{
			ExposedFunctions.Emplace(*RCFunction);
		}
	}
}

#if ENGINE_MINOR_VERSION > 0
FPortaPresetControllerDesc::FPortaPresetControllerDesc(const URCVirtualPropertyBase* RCController)
{
	DisplayName = RCController->DisplayName;
	Type = RCController->GetProperty()->GetCPPType();
}
#endif

FPortaPresetDesc::FPortaPresetDesc(const URemoteControlPreset* Preset)
{
	if (Preset)
	{
		Name = Preset->GetName();
		Path = Preset->GetPathName();
		ID = Preset->GetPresetId().ToString();
		Algo::Transform(Preset->Layout.GetGroups(), Groups, [Preset](const FRemoteControlPresetGroup& Group) { return FPortaPresetGroupDesc{ Preset, Group }; });
#if ENGINE_MINOR_VERSION > 0
		Algo::Transform(Preset->GetControllers(), Controllers, [Preset](const URCVirtualPropertyBase* Controller) { return FPortaPresetControllerDesc{ Controller }; });
#endif
	}
}

FPortaListPresetsResponse::FPortaListPresetsResponse(const TArray<FAssetData>& InPresets)
{
	Presets.Append(InPresets);
}

#if ENGINE_MINOR_VERSION >= 4
FPortaListPresetsResponse::FPortaListPresetsResponse(const TArray<FAssetData>& InPresets, const TArray<TWeakObjectPtr<URemoteControlPreset>>& InEmbeddedPresets)
{
	Presets.Append(InPresets);
	Presets.Append(InEmbeddedPresets);
}
#endif

FARFilter FPortaRCAssetFilter::ToARFilter() const
{

	FARFilter Filter;
	Filter.PackageNames = PackageNames;
	for (auto ClassName : ClassNames)
	{
		auto NameStr = ClassName.ToString();
		//this is a hack to keep backwards compatibility with 5.0
		if (NameStr == TEXT("LevelSequence"))
		{
			Filter.ClassPaths.Add(FTopLevelAssetPath("/Script/LevelSequence", "LevelSequence"));
		}
		else
		{
			Filter.ClassPaths.Add(FTopLevelAssetPath(NameStr));
		}
	}
	for (auto Exclusion : RecursiveClassesExclusionSet)
	{
		Filter.RecursiveClassPathsExclusionSet.Add(FTopLevelAssetPath(Exclusion.ToString()));
	}
	Filter.bRecursiveClasses = RecursiveClasses;
	Filter.PackagePaths = PackagePaths;

	// Default to a recursive search at root if no filter is specified.
	if (Filter.PackageNames.Num() == 0
		&& Filter.ClassPaths.Num() == 0
		&& Filter.PackagePaths.Num() == 0)
	{
		Filter.PackagePaths = { FName("/Game") };
		Filter.bRecursivePaths = true;
	}
	else
	{
		Filter.PackagePaths = PackagePaths;
		Filter.bRecursivePaths = RecursivePaths;
	}

	return Filter;
}
