#pragma once

#include "CoreMinimal.h"
#include "RemoteControlPreset.h"
#include "AssetRegistry/ARFilter.h"
#include "Runtime/Launch/Resources/Version.h"
#include "AssetRegistry/AssetData.h"
#if ENGINE_MINOR_VERSION > 0
#include "RCVirtualProperty.h"
#endif
#include "PortaStructs.generated.h"

USTRUCT()
struct PORTAINTERFACE_API FPortaPropertyDesc
{
	GENERATED_BODY()

	FPortaPropertyDesc() = default;

	FPortaPropertyDesc(const FProperty* Property);

	UPROPERTY()
	FString Name;

	UPROPERTY()
	FString Description;

	UPROPERTY()
	FString Type;
	UPROPERTY()
	FString ContainerType;

	UPROPERTY()
	FString KeyType;

	UPROPERTY()
	TMap<FName, FString> Metadata;
};

USTRUCT()
struct PORTAINTERFACE_API FPortaFunctionDesc
{
	GENERATED_BODY()

	FPortaFunctionDesc() = default;

	FPortaFunctionDesc(const UFunction* Function);

	UPROPERTY()
	FString Name;

	UPROPERTY()
	FString Description;

	UPROPERTY()
	TArray<FPortaPropertyDesc> Arguments;

};


USTRUCT()
struct PORTAINTERFACE_API FPortaPresetPropertyDesc
{
	GENERATED_BODY()

	FPortaPresetPropertyDesc() = default;

	FPortaPresetPropertyDesc(const FRemoteControlProperty& RCProperty);

	UPROPERTY()
	FName DisplayName;

	UPROPERTY()
	FString ID;

	UPROPERTY()
	FPortaPropertyDesc UnderlyingProperty;
};

USTRUCT()
struct PORTAINTERFACE_API FPortaObjectDesc
{
	GENERATED_BODY()

	FPortaObjectDesc() = default;
	FPortaObjectDesc(UObject* InObject)
	{
		checkSlow(InObject);

#if WITH_EDITOR
		if (AActor* Actor = Cast<AActor>(InObject))
		{
			Name = Actor->GetActorLabel();
		}
		else
#endif
		{
			// Get the class name when dealing with BP libraries and subsystems. 
			Name = InObject->HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject) ? InObject->GetClass()->GetName() : InObject->GetName();
		}

		Class = InObject->GetClass()->GetName();
		Path = InObject->GetPathName();
	}


	UPROPERTY()
	FString Name;

	UPROPERTY()
	FString Class;

	UPROPERTY()
	FString Path;

};

USTRUCT()
struct PORTAINTERFACE_API FPortaPresetFunctionDesc
{
	GENERATED_BODY()

	FPortaPresetFunctionDesc() = default;
	FPortaPresetFunctionDesc(const FRemoteControlFunction& Function);

	UPROPERTY()
	FName DisplayName;

	UPROPERTY()
	FString ID;

	UPROPERTY()
	FPortaFunctionDesc UnderlyingFunction;

	UPROPERTY()
	TArray<FPortaObjectDesc> OwnerObjects;
};

USTRUCT()
struct PORTAINTERFACE_API FPortaPresetGroupDesc
{
	GENERATED_BODY()

	FPortaPresetGroupDesc() = default;

	FPortaPresetGroupDesc(const URemoteControlPreset* Preset, const FRemoteControlPresetGroup& Group);

	UPROPERTY()
	FName Name;

	UPROPERTY()
	TArray<FPortaPresetPropertyDesc> ExposedProperties;

	UPROPERTY()
	TArray<FPortaPresetFunctionDesc> ExposedFunctions;

private:
	void AddExposedField(const URemoteControlPreset* Preset, const FGuid& EntityId);
};

USTRUCT()
struct PORTAINTERFACE_API FPortaPresetControllerDesc
{
	GENERATED_BODY()

	FPortaPresetControllerDesc() = default;

#if ENGINE_MINOR_VERSION > 0
	FPortaPresetControllerDesc(const URCVirtualPropertyBase* RCController);
#endif

	UPROPERTY()
	FName DisplayName;

	UPROPERTY()
	FString Type;
};


USTRUCT()
struct PORTAINTERFACE_API FPortaShortPresetDesc
{
	GENERATED_BODY()

	FPortaShortPresetDesc() = default;

	FPortaShortPresetDesc(const FAssetData& PresetAsset)
	{
		Name = PresetAsset.AssetName;
	}

#if ENGINE_MINOR_VERSION >= 4
	FPortaShortPresetDesc(const TWeakObjectPtr<URemoteControlPreset>& PresetAsset)
	{
		check(PresetAsset.IsValid());
		Name = PresetAsset->GetPresetName();
	}
#endif

	UPROPERTY()
	FName Name;
};

USTRUCT(BlueprintType)
struct PORTAINTERFACE_API FPortaPresetDesc
{
	GENERATED_BODY()
	FPortaPresetDesc() = default;

	FPortaPresetDesc(const URemoteControlPreset* Preset);

	UPROPERTY()
	FString Name;

	UPROPERTY()
	FString Path;

	UPROPERTY()
	FString ID;

	UPROPERTY()
	TArray<FPortaPresetGroupDesc> Groups;

	UPROPERTY()
	TArray<FPortaPresetControllerDesc> Controllers;
};

USTRUCT()
struct PORTAINTERFACE_API FPortaListPresetsResponse
{
	GENERATED_BODY()

	FPortaListPresetsResponse() = default;

	FPortaListPresetsResponse(const TArray<FAssetData>& InPresets);
#if ENGINE_MINOR_VERSION >= 4
	FPortaListPresetsResponse(const TArray<FAssetData>& InPresets, const TArray<TWeakObjectPtr<URemoteControlPreset>>& InEmbeddedPresets);
#endif

	UPROPERTY()
	TArray<FPortaShortPresetDesc> Presets;
};

USTRUCT()
struct PORTAINTERFACE_API FPortaPresetResponse
{
	GENERATED_BODY()
	FPortaPresetResponse() = default;

	FPortaPresetResponse(const URemoteControlPreset* InPreset) : Preset(InPreset) {}

	UPROPERTY()
	FPortaPresetDesc Preset;
};

USTRUCT()
struct PORTAINTERFACE_API FPortaErrorResponse
{
	GENERATED_BODY()
	FPortaErrorResponse() = default;

	FPortaErrorResponse(const FString& InError) : Error(InError) {}

	UPROPERTY()
	FString Error;

};

USTRUCT()
struct PORTAINTERFACE_API FChannelSubscribe
{
	GENERATED_BODY()

	UPROPERTY()
	FString channel;
};

USTRUCT()
struct PORTAINTERFACE_API FClientEvent
{
	GENERATED_BODY()

	UPROPERTY()
	FString channel;

	UPROPERTY()
	FString event;
};

USTRUCT()
struct PORTAINTERFACE_API FPortaRCAssetFilter
{
	GENERATED_BODY()

	FPortaRCAssetFilter() = default;

	FARFilter ToARFilter() const;

	/** The filter component for package names */
	UPROPERTY()
	TArray<FName> PackageNames;

	/** The filter component for package paths */
	UPROPERTY()
	TArray<FName> PackagePaths;

	/** The filter component for class names. Instances of the specified classes, but not subclasses (by default), will be included. Derived classes will be included only if bRecursiveClasses is true. */
	UPROPERTY()
	TArray<FName> ClassNames;

	/** Only if bRecursiveClasses is true, the results will exclude classes (and subclasses) in this list */
	UPROPERTY()
	TSet<FName> RecursiveClassesExclusionSet;

	/** Only if EnableBlueprintNativeClassFiltering is true, resulting asset will be filtered for dependants of classes in this list. */
	UPROPERTY()
	TArray<FName> NativeParentClasses;

	/** If true, subclasses of ClassNames will also be included and RecursiveClassesExclusionSet will be excluded. */
	UPROPERTY()
	bool RecursiveClasses = false;

	/** If true, PackagePath components will be recursive */
	UPROPERTY()
	bool RecursivePaths = false;

	/** When dealing with blueprint classes, you might want to filter for a base class which can't be picked by asset registry if you derive from a blueprint class */
	UPROPERTY()
	bool EnableBlueprintNativeClassFiltering = false;
};

USTRUCT()
struct PORTAINTERFACE_API FPortaSearchAssetRequest
{
	GENERATED_BODY()

	FPortaSearchAssetRequest()
		: Limit(100)
	{
	}

	/**
	 * The search query which will be compared with the asset names.
	 */
	UPROPERTY()
	FString Query;

	/*
	 * The filter applied to this search.
	 */
	UPROPERTY()
	FPortaRCAssetFilter Filter;

	/**
	 * The maximum number of search results returned.
	 */
	UPROPERTY()
	int32 Limit;
};

USTRUCT()
struct PORTAINTERFACE_API FPortaAssetDescription
{
	GENERATED_BODY()

	FPortaAssetDescription() = default;
	FPortaAssetDescription(const FAssetData& InAsset)
		: Name(InAsset.AssetName)
		, Class(InAsset.AssetClassPath.ToString())
		, Path(InAsset.GetObjectPathString())
	{
	}

	UPROPERTY()
	FName Name;

	UPROPERTY()
	FName Class;

	UPROPERTY()
	FName Path;
};

USTRUCT()
struct PORTAINTERFACE_API FPortaSearchAssetResponse
{
	GENERATED_BODY()

	FPortaSearchAssetResponse() = default;

	FPortaSearchAssetResponse(const TArray<FAssetData>& InAssets)
	{
		Assets.Append(InAssets);
	}

	UPROPERTY()
	TArray<FPortaAssetDescription> Assets;
};

USTRUCT()
struct PORTAINTERFACE_API FPortaExposedPropertyDescription
{
	GENERATED_BODY()

	FPortaExposedPropertyDescription() = default;

	FPortaExposedPropertyDescription(const FRemoteControlProperty& RCProperty)
		: DisplayName(RCProperty.GetLabel())
	{
		UnderlyingProperty = RCProperty.GetProperty();
		Metadata = RCProperty.GetMetadata();
		ID = RCProperty.GetId().ToString();
		OwnerObjects.Append(RCProperty.GetBoundObjects());
	}

	UPROPERTY()
	FName DisplayName;

	UPROPERTY()
	FString ID;

	UPROPERTY()
	FPortaPropertyDesc UnderlyingProperty;

	UPROPERTY()
	TMap<FName, FString> Metadata;

	UPROPERTY()
	TArray<FPortaObjectDesc> OwnerObjects;
};
