// ©2023 Batty Bovine Productions, LLC. All Rights Reserved.

#include "ComboActionSearchManager.h"

#include "ComboActionGraph.h"
#include "ComboInputAssets.h"

#include "AssetRegistry/AssetRegistryModule.h"
#include "Ed/EdComboActionGraph.h"
#include "Nodes/ComboActionGraphNode.h"
#include "Nodes/ComboActionGraphNode_ActionNodeBase.h"
#include "Search/ComboActionSearchFilter.h"

#define LOCTEXT_NAMESPACE "ComboActionSearchManager"

FComboActionSearchManager *FComboActionSearchManager::Instance = nullptr;


bool FComboActionSearchManager::QueryGraphNode(const FComboActionSearchFilter &SearchFilter, const UEdComboActionGraphNode *InGraphNode, const TSharedPtr<FComboActionSearchResult> &OutParentNode) const
{
	if (SearchFilter.SearchString.IsEmpty() || !OutParentNode.IsValid() || !IsValid(InGraphNode))
	{
		return false;
	}
	
	bool bContainsSearchString = false;
	const UComboActionGraphNode *Node = InGraphNode->ComboActionGraphNode;

	if (Node == nullptr)
	{
		return false;
	}
	
	const FString NodeType = Node->NodeTypeName.ToString();
	
	const FText DisplayText = FText::Format(LOCTEXT("ComboActionNodeCategory", "Found results in {0}"), FText::FromString(NodeType));

	const TSharedPtr<FComboActionSearchResult_GraphNode> TreeGraphNode = MakeShared<FComboActionSearchResult_GraphNode>(DisplayText, OutParentNode);
	TreeGraphNode->SetCategory(FText::FromString(NodeType));
	TreeGraphNode->SetGraphNode(InGraphNode);
	if (bContainsSearchString)
	{
		OutParentNode->AddChild(TreeGraphNode);
	}

	// Search by Title
	if (SearchFilter.bIncludeNodeTitle)
	{
		if (Node->NodeTitle.ToString().Contains(SearchFilter.SearchString))
		{
			bContainsSearchString = true;
			MakeChildTextNode
			(
				TreeGraphNode,
				FText::FromName(FName(Node->NodeTitle.ToString() )),
				LOCTEXT("NodeTitleKey", "Node Title"),
				TEXT("Node Title")
			);
		}
	}

	// Search by NodeTypeName
	if (SearchFilter.bIncludeNodeType)
	{
		if (Node->NodeTypeName.ToString().Contains(SearchFilter.SearchString))
		{
			bContainsSearchString = true;
			this->MakeChildTextNode(
				TreeGraphNode,
				FText::FromName(FName(Node->NodeTypeName.ToString() )),
				LOCTEXT("NodeTypeKey", "Node Type"),
				TEXT("Node Type")
			);
		}
	}

	// Search by Node Data
	if (SearchFilter.bIncludeNodeData)
	{
		if (const UComboActionGraphNode_ActionNodeBase *ActionNodeBase = Cast<UComboActionGraphNode_ActionNodeBase>(Node))
		{
			if (ActionNodeBase->GetComboInput()->GetName().Contains(SearchFilter.SearchString))
			{
				bContainsSearchString = true;
				this->MakeChildTextNode(
					TreeGraphNode,
					FText::FromName(FName(Node->NodeTypeName.ToString())),
					LOCTEXT("NodeDataRowKey", "Node Data"),
					TEXT("Node Data")
				);
			}
		}
	}

	// Search by GUID
	if (SearchFilter.bIncludeNodeGUID)
	{
		const FString FoundGUID = Node->GetNodeGUID().ToString();
		if (FoundGUID.Contains(SearchFilter.SearchString))
		{
			bContainsSearchString = true;
			MakeChildTextNode
			(
				TreeGraphNode,
				FText::FromString(FoundGUID),
				LOCTEXT("NodeGUID", "Node GUID"),
				TEXT("Node GUID")
			);
		}
	}
	
	if (bContainsSearchString)
	{
		OutParentNode->AddChild(TreeGraphNode);
	}

	return bContainsSearchString;
}

bool FComboActionSearchManager::QueryNodeDecorators(const FComboActionSearchFilter &SearchFilter, const FComboActionDecorator &InDecorator, const TSharedPtr<FComboActionSearchResult> &OutParentNode, int32 DecoratorIndex, FName DecoratorMemberName) const
{
	if (SearchFilter.SearchString.IsEmpty() || !OutParentNode.IsValid())
	{
		return false;
	}
	bool bContainsSearchString = false;

	if (InDecorator.DecoratorType == nullptr) return false;

	// Search by Decorator Name
	if (InDecorator.DecoratorType->GetName().Contains(SearchFilter.SearchString))
	{
		bContainsSearchString = true;

		FString DecoratorName = InDecorator.DecoratorType->GetClass()->GetName();
		// Format Name
		{
			if (DecoratorName.Contains(TEXT("_GEN_VARIABLE")))
			{
				DecoratorName.ReplaceInline(TEXT("_GEN_VARIABLE"), TEXT(""));
			}
			if(DecoratorName.EndsWith(TEXT("_C")) && DecoratorName.StartsWith(TEXT("Default__")))
			{
				DecoratorName.RightChopInline(9);
				DecoratorName.LeftChopInline(2);
			}
			if (DecoratorName.EndsWith(TEXT("_C")))
			{
				DecoratorName.LeftChopInline(2);
			}
		}
		
		const FText Category = FText::Format
		(
			LOCTEXT("DecoratorName", "Node Decorator: {0} at Index: {1}"),
			FText::FromString(DecoratorName), FText::AsNumber(DecoratorIndex)
		);
		MakeChildTextNode
		(
			OutParentNode,
			FText::FromString(DecoratorName),
			Category,
			Category.ToString()
		);
	}

	return bContainsSearchString;
}

bool FComboActionSearchManager::QuerySingleAction(const FComboActionSearchFilter &SearchFilter, const UComboActionGraph *InAction, TSharedPtr<FComboActionSearchResult> &OutParentNode)
{
	if (SearchFilter.SearchString.IsEmpty() || !OutParentNode.IsValid() || !IsValid(InAction))
	{
		return false;
	}

	const UEdComboActionGraph *Graph = CastChecked<UEdComboActionGraph>(InAction->EdGraph);

	const TSharedPtr<FComboActionSearchResult_ActionNode> TreeActionNode = MakeShared<FComboActionSearchResult_ActionNode>(FText::FromString(InAction->GetPathName()), OutParentNode);
	TreeActionNode->SetActionGraph(Graph);

	// Find in GraphNodes
	bool bFoundInAction = false;
	const TArray<UEdGraphNode*> &AllGraphNodes = Graph->Nodes;
	for (UEdGraphNode *Node : AllGraphNodes)
	{
		bool bFoundInNode = false;
		if (const UEdComboActionGraphNode *GraphNode = Cast<UEdComboActionGraphNode>(Node))
		{
			bFoundInNode = this->QueryGraphNode(SearchFilter, GraphNode, TreeActionNode);
		}

		// Found at least one match in one of the nodes.
		bFoundInAction = bFoundInNode || bFoundInAction;
	}

	if (bFoundInAction)
	{
		OutParentNode->AddChild(TreeActionNode);
	}

	return bFoundInAction;
}

void FComboActionSearchManager::Initialize(TSharedPtr<FWorkspaceItem> ParentTabCategory)
{
	// Must ensure we do not attempt to load the AssetRegistry Module while saving a package, however, if it is loaded already we can safely obtain it
	this->AssetRegistry = &FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry").Get();

	this->OnAssetAddedHandle = this->AssetRegistry->OnAssetAdded().AddRaw(this, &FComboActionSearchManager::HandleOnAssetAdded);
	this->OnAssetRemovedHandle = this->AssetRegistry->OnAssetRemoved().AddRaw(this, &FComboActionSearchManager::HandleOnAssetRemoved);
	this->OnAssetRenamedHandle = this->AssetRegistry->OnAssetRenamed().AddRaw(this, &FComboActionSearchManager::HandleOnAssetRenamed);

	if (this->AssetRegistry->IsLoadingAssets())
	{
		this->OnFilesLoadedHandle = this->AssetRegistry->OnFilesLoaded().AddRaw(this, &FComboActionSearchManager::HandleOnAssetRegistryFilesLoaded);
	}
	else
	{
		this->HandleOnAssetRegistryFilesLoaded();
	}
	this->OnAssetLoadedHandle = FCoreUObjectDelegates::OnAssetLoaded.AddRaw(this, &FComboActionSearchManager::HandleOnAssetLoaded);
}

void FComboActionSearchManager::UnInitialize()
{
	if (this->AssetRegistry)
	{
		if (this->OnAssetAddedHandle.IsValid())
		{
			this->AssetRegistry->OnAssetAdded().Remove(this->OnAssetAddedHandle);
			this->OnAssetAddedHandle.Reset();
		}
		if (this->OnAssetRemovedHandle.IsValid())
		{
			this->AssetRegistry->OnAssetRemoved().Remove(this->OnAssetRemovedHandle);
			this->OnAssetRemovedHandle.Reset();
		}
		if (this->OnFilesLoadedHandle.IsValid())
		{
			this->AssetRegistry->OnFilesLoaded().Remove(this->OnFilesLoadedHandle);
			this->OnFilesLoadedHandle.Reset();
		}
		if (this->OnAssetRenamedHandle.IsValid())
		{
			this->AssetRegistry->OnAssetRenamed().Remove(this->OnAssetRenamedHandle);
			this->OnAssetRenamedHandle.Reset();
		}
	}

	if (this->OnAssetLoadedHandle.IsValid())
	{
		FCoreUObjectDelegates::OnAssetLoaded.Remove(this->OnAssetLoadedHandle);
		this->OnAssetLoadedHandle.Reset();
	}
}


#undef LOCTEXT_NAMESPACE
