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

#include "Ed/EdComboActionGraph.h"

#include "ComboActionGraph.h"
#include "Ed/AssetEditor_ComboActionGraph.h"
#include "Ed/EdComboActionGraphEdge.h"
#include "Ed/EdComboActionGraphNode.h"
#include "Nodes/ComboActionGraphEdge.h"
#include "Nodes/ComboActionGraphNode.h"
//#include "Helpers/MounteaDialogueGraphEditorHelpers.h"
//#include "Helpers/MounteaDialogueGraphEditorUtilities.h"
//#include "Helpers/MounteaDialogueSystemEditorBFC.h"

DEFINE_LOG_CATEGORY(LogEdComboActionGraph);


void UEdComboActionGraph::RebuildComboActionGraph()
{
	UE_LOG(LogEdComboActionGraph, Warning, TEXT("UEdComboActionGraph::RebuildComboActionGraph has been called"));

	UComboActionGraph *Graph = GetComboActionGraph();

	this->Clear();

	for (const TObjectPtr<UEdGraphNode> &Node : this->Nodes)
	{
		if (UEdComboActionGraphNode *EdNode = Cast<UEdComboActionGraphNode>(Node))
		{
			if (EdNode->ComboActionGraphNode == nullptr)
				continue;

			UComboActionGraphNode *ComboActionGraphNode = EdNode->ComboActionGraphNode;

			this->NodeMap.Add(ComboActionGraphNode, EdNode);

			Graph->AllNodes.Add(ComboActionGraphNode);

			//EdNode->SetDialogueNodeIndex( Graph->AllNodes.Find(EdNode->ComboActionGraphNode) );

			for (int PinIdx = 0; PinIdx < EdNode->Pins.Num(); ++PinIdx)
			{
				UEdGraphPin* Pin = EdNode->Pins[PinIdx];

				if (Pin->Direction != EEdGraphPinDirection::EGPD_Output)
					continue;

				for (int LinkToIdx = 0; LinkToIdx < Pin->LinkedTo.Num(); ++LinkToIdx)
				{
					UComboActionGraphNode *ChildNode = nullptr;
					if (UEdComboActionGraphNode *EdNode_Child = Cast<UEdComboActionGraphNode>(Pin->LinkedTo[LinkToIdx]->GetOwningNode()))
					{
						ChildNode = EdNode_Child->ComboActionGraphNode;
					}
					else if (UEdComboActionGraphEdge *EdNode_Edge = Cast<UEdComboActionGraphEdge>(Pin->LinkedTo[LinkToIdx]->GetOwningNode()))
					{
						UEdComboActionGraphNode *Child = EdNode_Edge->GetEndNode();;
						if (Child != nullptr)
						{
							ChildNode = Child->ComboActionGraphNode;
						}
					}

					if (ChildNode != nullptr)
					{
						ComboActionGraphNode->ChildrenNodes.Add(ChildNode);

						ChildNode->ParentNodes.Add(ComboActionGraphNode);
					}
					else
					{
						UE_LOG(LogEdComboActionGraph, Error, TEXT("[RebuildComboActionGraph] Can't find child node"));
					}
				}
			}
		}
		else if (UEdComboActionGraphEdge *EdgeNode = Cast<UEdComboActionGraphEdge>(Node))
		{
			UEdComboActionGraphNode *StartNode = EdgeNode->GetStartNode();
			UEdComboActionGraphNode *EndNode = EdgeNode->GetEndNode();
			UComboActionGraphEdge *Edge = EdgeNode->ComboActionGraphEdge;

			if (StartNode == nullptr || EndNode == nullptr || Edge == nullptr)
			{
				UE_LOG(LogEdComboActionGraph, Error, TEXT("[RebuildComboActionGraph] Add edge failed."));
				continue;
			}

			this->EdgeMap.Add(Edge, EdgeNode);

			Edge->SetGraph(Graph);
			Edge->Rename(nullptr, Graph, REN_DontCreateRedirectors | REN_DoNotDirty);
			StartNode->ComboActionGraphNode->Edges.Add(EndNode->ComboActionGraphNode, Edge);
			Edge->SetStartNode(StartNode->ComboActionGraphNode);
			Edge->SetEndNode(EndNode->ComboActionGraphNode);
		}
	}

	for (UComboActionGraphNode *Node : Graph->AllNodes)
	{
		if (Node->ParentNodes.Num() == 0)
		{
			Graph->RootNodes.Add(Node);

			SortNodes(Node);
		}

		Node->Graph = Graph;
		Node->Rename(nullptr, Graph, REN_DontCreateRedirectors | REN_DoNotDirty);
	}

	Graph->RootNodes.Sort([&](const UComboActionGraphNode &L, const UComboActionGraphNode &R)
	{
		UEdComboActionGraphNode *EdNode_LNode = this->NodeMap[&L];
		UEdComboActionGraphNode *EdNode_RNode = this->NodeMap[&R];
		return EdNode_LNode->NodePosX < EdNode_RNode->NodePosX;
	});
}

UComboActionGraph *UEdComboActionGraph::GetComboActionGraph() const
{
	return CastChecked<UComboActionGraph>(GetOuter());
}

bool UEdComboActionGraph::Modify(bool bAlwaysMarkDirty)
{
	bool Rtn = Super::Modify(bAlwaysMarkDirty);

	this->GetComboActionGraph()->Modify();

	for (TObjectPtr<UEdGraphNode> &Node : this->Nodes)
	{
		Node->Modify();
	}

	return Rtn;
}

void UEdComboActionGraph::PostEditUndo()
{
	this->NotifyGraphChanged();
	
	Super::PostEditUndo();
}

void UEdComboActionGraph::SetDialogueEditorPtr(TWeakPtr<FAssetEditor_ComboActionGraph> NewPtr)
{
	this->ComboActionEditorPtr = NewPtr;
}

bool UEdComboActionGraph::JumpToNode(const UComboActionGraphNode *Node)
{
	//return FComboActionGraphEditorUtilities::OpenEditorAndJumpToGraphNode(this->ComboActionEditorPtr, *NodeMap.Find(Node));
	return false;
}

void UEdComboActionGraph::Clear()
{
	UComboActionGraph *Graph = this->GetComboActionGraph();

	Graph->ClearGraph();
	this->NodeMap.Reset();
	this->EdgeMap.Reset();

	for (int i = 0; i < this->Nodes.Num(); ++i)
	{
		if (UEdComboActionGraphNode *EdNode = Cast<UEdComboActionGraphNode>(Nodes[i]))
		{
			UComboActionGraphNode *MounteaDialogueGraphNode = EdNode->ComboActionGraphNode;
			MounteaDialogueGraphNode->ParentNodes.Reset();
			MounteaDialogueGraphNode->ChildrenNodes.Reset();
			MounteaDialogueGraphNode->Edges.Reset();
		}
	}
}

void UEdComboActionGraph::SortNodes(UComboActionGraphNode *RootNode)
{
	int Level = 0;
	TArray<UComboActionGraphNode*> CurrLevelNodes = { RootNode };
	TArray<UComboActionGraphNode*> NextLevelNodes;

	while (CurrLevelNodes.Num() != 0)
	{
		int32 LevelWidth = 0;
		for (int i = 0; i < CurrLevelNodes.Num(); ++i)
		{
			UComboActionGraphNode *Node = CurrLevelNodes[i];

			auto Comp = [&](const UComboActionGraphNode &L, const UComboActionGraphNode &R)
			{
				UEdComboActionGraphNode *EdNode_LNode = NodeMap[&L];
				UEdComboActionGraphNode *EdNode_RNode = NodeMap[&R];
				return EdNode_LNode->NodePosX < EdNode_RNode->NodePosX;
			};

			Node->ChildrenNodes.Sort(Comp);
			Node->ParentNodes.Sort(Comp);

			for (int j = 0; j < Node->ChildrenNodes.Num(); ++j)
			{
				NextLevelNodes.Add(Node->ChildrenNodes[j]);
			}
		}

		CurrLevelNodes = NextLevelNodes;
		NextLevelNodes.Reset();
		++Level;
	}
}
