#include "WingGraphActions.h"
#include "EdGraph/EdGraphSchema.h"
#include "BlueprintActionDatabase.h"
#include "BlueprintNodeSpawner.h"
#include "EdGraphSchema_K2.h"
#include "WingUtils.h"
#include "Kismet2/BlueprintEditorUtils.h"

FWingGraphAction::FWingGraphAction(TSharedPtr<FEdGraphSchemaAction> &iAction, UEdGraph *iGraph)
{
	Action = iAction;
	Graph = iGraph;
	FString Category = Action->GetCategory().ToString();
	FString MenuName = Action->GetMenuDescription().ToString();
	Name = WingUtils::StandardizeMenuItem(Category + TEXT("|") + MenuName);
	Keywords = Action->GetKeywords().ToString();
}

FWingGraphAction::FWingGraphAction(UBlueprintNodeSpawner *iSpawner, UEdGraph *iGraph)
{
	Spawner = iSpawner;
	Graph = iGraph;
	const FBlueprintActionUiSpec& UiSpec = Spawner->PrimeDefaultUiSpec();
	FString Category = UiSpec.Category.ToString();
	FString MenuName = UiSpec.MenuName.ToString();
	Name = WingUtils::StandardizeMenuItem(Category + TEXT("|") + MenuName);
	Keywords = Spawner->PrimeDefaultUiSpec().Keywords.ToString();
}

UEdGraphNode* FWingGraphAction::Execute(const FVector2D &Location) const
{
	if (Spawner)
	{
		return Spawner->Invoke(Graph, IBlueprintNodeBinder::FBindingSet(), Location);
	}
	else
	{
		return Action->PerformAction(Graph, nullptr, Location, /*bSelectNewNode=*/false);
	}
}

TArray<FWingGraphAction*> FWingGraphActions::Search(const FString &Query, int32 MaxResults, bool Exact)
{
	FString ExtQuery = FString::Printf(TEXT("*%s*"), *Query.Replace(TEXT(" "), TEXT("*")));
	TArray<FWingGraphAction*> Results;
	for (FWingGraphAction &Result : Actions)
	{
		if (Results.Num() == MaxResults) break;
		if (Exact)
		{
			if (Result.Name.Equals(Query, ESearchCase::IgnoreCase))
				Results.Emplace(&Result);
		}
		else
		{
			if (Result.Name.MatchesWildcard(ExtQuery, ESearchCase::IgnoreCase) ||
				Result.Keywords.MatchesWildcard(ExtQuery, ESearchCase::IgnoreCase))
				Results.Emplace(&Result);
		}
	}
	return Results;
}

void FWingGraphActions::CollectActions()
{
	FGraphContextMenuBuilder ContextMenuBuilder(Graph);
	Graph->GetSchema()->GetGraphContextActions(ContextMenuBuilder);
	for (int32 i = 0; i < ContextMenuBuilder.GetNumActions(); i++)
	{
		Actions.Emplace(ContextMenuBuilder.GetSchemaAction(i), Graph);
	}
}

void FWingGraphActions::CollectSpawners()
{
	for (const auto& Pair : FBlueprintActionDatabase::Get().GetAllActions())
	{
		for (UBlueprintNodeSpawner* Spawner : Pair.Value)
		{
			if (Spawner->NodeClass)
			{
				UEdGraphNode* NodeCDO = CastChecked<UEdGraphNode>(Spawner->NodeClass->ClassDefaultObject);
				if (NodeCDO->IsCompatibleWithGraph(Graph))
				{
					Actions.Emplace(Spawner, Graph);
				}
			}
		}
	}
}

FWingGraphActions::FWingGraphActions(UEdGraph *iGraph)
{
	Graph = iGraph;
	if (Cast<UEdGraphSchema_K2>(Graph->GetSchema()))
	{
		CollectSpawners();
	}
	else
	{
		CollectActions();
	}
}
