#pragma once

#include "CoreMinimal.h"
#include "WingServer.h"
#include "WingBasics.h"
#include "WingTypes.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(EditAnywhere, meta=(Description="Maximum number of results"))
	int32 Limit = 100;

	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 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;
			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))
				WingOut::Stdout.Printf(TEXT("%s (%s, User-Defined)\n"), *Info.Short, *Info.BroadCategory.ToString());
			else
				WingOut::Stdout.Printf(TEXT("%s (%s)\n"), *Info.Short, *Info.BroadCategory.ToString());
		}
		if (Count >= Limit)
		{
			WingOut::Stdout.Printf(TEXT("Search limit reached, raise it with Limit=\n"));
		}
	}
};
