#pragma once

#include "CoreMinimal.h"
#include "WingServer.h"
#include "WingHandler.h"
#include "WingTypes.h"
#include "EdGraphSchema_K2.h"
#include "GameFramework/Actor.h"
#include "Components/ActorComponent.h"
#include "Blueprint/UserWidget.h"
#include "GameFramework/Pawn.h"
#include "Engine/DataAsset.h"
#include "TypeName_Search.generated.h"


// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------

UCLASS()
class UWing_TypeName_Search : public UWingHandler
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere, meta=(Description="Substring filter for type names"))
	FString Query;

	UPROPERTY(meta=(Optional, Description="Maximum number of results"))
	int32 Limit = 100;

	UPROPERTY(meta=(Optional, Description="If true, include all types, not just BlueprintType/Blueprintable ones"))
	bool Exhaustive = false;

	virtual void Register() override
	{
		UWingServer::AddHandler(this,
			TEXT("Search for type names usable in pin type specifications. "
			"Returns short names that can be used with commands like Blueprint_ChangeVariableType."));
	}
	static bool IsProjectDefined(const UWingTypes::Info& Info)
	{
		return Info.IsUserDefined && Info.PinSubCategoryObject.StartsWith(TEXT("/Game/"));
	}

	static FString BroadCategory(const UWingTypes::Info& Info)
	{
		if (Info.PinSubCategoryObject.IsEmpty()) return TEXT("Primitive");
		if (Info.PinCategory == UEdGraphSchema_K2::PC_Enum) return TEXT("Enum");
		if (Info.PinCategory == UEdGraphSchema_K2::PC_Struct) return TEXT("Struct");
		if (Info.PinCategory == UEdGraphSchema_K2::PC_Interface) return TEXT("Interface");
		if (Info.NativeParent)
		{
			if (Info.NativeParent->IsChildOf(UUserWidget::StaticClass())) return TEXT("Widget");
			if (Info.NativeParent->IsChildOf(UActorComponent::StaticClass())) return TEXT("ActorComponent");
			if (Info.NativeParent->IsChildOf(APawn::StaticClass())) return TEXT("Pawn");
			if (Info.NativeParent->IsChildOf(AActor::StaticClass())) return TEXT("Actor");
			if (Info.NativeParent->IsChildOf(UDataAsset::StaticClass())) return TEXT("DataAsset");
		}
		return TEXT("Object");
	}

	static int Importance(const UWingTypes::Info& Info)
	{
		if (IsProjectDefined(Info)) return 1;
		if (Info.PinSubCategoryObject.IsEmpty()) return 2;
		if (Info.PinSubCategoryObject.StartsWith(TEXT("/Script/CoreUObject."))) return 3;
		if (Info.PinSubCategoryObject.StartsWith(TEXT("/Script/Engine."))) return 4;
		return 5;
	}

	virtual void Handle() override
	{
		const TMap<FString, UWingTypes::Info>& AllTypes = UWingTypes::GetAllTypes();

		TArray<const UWingTypes::Info*> Matches;
		for (const auto& Pair : AllTypes)
		{
			const UWingTypes::Info& Info = Pair.Value;
			if (!Info.Short.Contains(Query, ESearchCase::IgnoreCase)) continue;
			if (!Exhaustive && !UWingTypes::IsBlueprintType(Info) && !UWingTypes::IsBlueprintable(Info)) continue;
			Matches.Add(&Info);
		}

		Matches.Sort([](const UWingTypes::Info& A, const UWingTypes::Info& B)
		{
			int IA = Importance(A), IB = Importance(B);
			if (IA != IB) return IA < IB;
			return A.Short < B.Short;
		});

		int32 Count = FMath::Min(Matches.Num(), Limit);
		for (int32 i = 0; i < Count; ++i)
		{
			const UWingTypes::Info& Info = *Matches[i];
			if (IsProjectDefined(Info))
				UWingServer::Printf(TEXT("%s (%s, User-Defined)\n"), *Info.Short, *BroadCategory(Info));
			else
				UWingServer::Printf(TEXT("%s (%s)\n"), *Info.Short, *BroadCategory(Info));
		}
		if (Count >= Limit)
		{
			UWingServer::Printf(TEXT("Search limit reached, raise it with Limit=\n"));
		}
	}
};
