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

#pragma once

#include "CoreMinimal.h"

#include "ComboActionGraphSchema.generated.h"


class FComboActionNodeVisitorCycleChecker
{
public:
	/** Check whether a loop in the graph would be caused by linking the passed-in nodes */
	bool CheckForLoop(UEdGraphNode *StartNode, UEdGraphNode *EndNode)
	{
		VisitedNodes.Add(StartNode);

		if (!QuiCheckDirectConnections(StartNode, EndNode))
		{
			return false;
		}

		return TraverseInputNodesToRoot(EndNode);
	}

private:
	bool TraverseInputNodesToRoot(UEdGraphNode *Node)
	{
		VisitedNodes.Add(Node);

		for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex)
		{
			UEdGraphPin *MyPin = Node->Pins[PinIndex];

			if (MyPin->Direction == EGPD_Output)
			{
				for (int32 LinkedPinIndex = 0; LinkedPinIndex < MyPin->LinkedTo.Num(); ++LinkedPinIndex)
				{
					UEdGraphPin *OtherPin = MyPin->LinkedTo[LinkedPinIndex];
					if (OtherPin)
					{
						UEdGraphNode *OtherNode = OtherPin->GetOwningNode();
						if (VisitedNodes.Contains(OtherNode))
						{
							return false;
						}
						else
						{
							return TraverseInputNodesToRoot(OtherNode);
						}
					}
				}
			}
		}

		return true;
	}

	bool QuiCheckDirectConnections(UEdGraphNode *A, UEdGraphNode *B)
	{
		for (auto Itr : B->Pins)
		{
			if (A->Pins.Contains(Itr))
			{
				return false;
			}
		}

		for (auto Itr : A->Pins)
		{
			if (B->Pins.Contains(Itr))
			{
				return false;
			}
		}

		return true;
	}

	TSet<UEdGraphNode *> VisitedNodes;
};

/** Action to add a node to the graph */
USTRUCT()
struct FAssetSchemaAction_ComboActionGraphSchema_NewNode : public FEdGraphSchemaAction
{
	GENERATED_BODY();

public:
	FAssetSchemaAction_ComboActionGraphSchema_NewNode() : NodeTemplate(nullptr){}

	FAssetSchemaAction_ComboActionGraphSchema_NewNode(const FText &InNodeCategory, const FText &InMenuDesc, const FText &InToolTip, const int32 InGrouping)
		: FEdGraphSchemaAction(InNodeCategory, InMenuDesc, InToolTip, InGrouping), NodeTemplate(nullptr){}

	virtual UEdGraphNode *PerformAction(class UEdGraph *ParentGraph, UEdGraphPin *FromPin, const FVector2D Location, bool bSelectNewNode = true) override;
	virtual void AddReferencedObjects(FReferenceCollector &Collector) override;

	class UEdComboActionGraphNode *NodeTemplate;
};

USTRUCT()
struct FAssetSchemaAction_ComboActionGraphSchema_NewEdge : public FEdGraphSchemaAction
{
	GENERATED_BODY();

public:
	FAssetSchemaAction_ComboActionGraphSchema_NewEdge() : NodeTemplate(nullptr)
	{}

	FAssetSchemaAction_ComboActionGraphSchema_NewEdge(const FText &InNodeCategory, const FText &InMenuDesc, const FText &InToolTip, const int32 InGrouping)
		: FEdGraphSchemaAction(InNodeCategory, InMenuDesc, InToolTip, InGrouping), NodeTemplate(nullptr)
	{}

	virtual UEdGraphNode *PerformAction(class UEdGraph *ParentGraph, UEdGraphPin *FromPin, const FVector2D Location, bool bSelectNewNode = true) override;
	virtual void AddReferencedObjects(FReferenceCollector &Collector) override;

	class UEdComboActionGraphEdge *NodeTemplate;
};


UCLASS(MinimalAPI)
class UComboActionGraphSchema : public UEdGraphSchema
{
	GENERATED_BODY()
	
public:
	void GetBreakLinkToSubMenuActions(class UToolMenu* Menu, class UEdGraphPin* InGraphPin);

	virtual EGraphType GetGraphType(const UEdGraph* TestEdGraph) const override { return EGraphType::GT_StateMachine; }

	virtual void GetGraphContextActions(FGraphContextMenuBuilder& ContextMenuBuilder) const override;

	virtual void GetContextMenuActions(class UToolMenu* Menu, class UGraphNodeContextMenuContext* Context) const override;

	virtual const FPinConnectionResponse CanCreateConnection(const UEdGraphPin* A, const UEdGraphPin* B) const override;

	virtual bool CreateAutomaticConversionNodeAndConnections(UEdGraphPin* A, UEdGraphPin* B) const override;

	virtual class FConnectionDrawingPolicy* CreateConnectionDrawingPolicy(int32 InBackLayerID, int32 InFrontLayerID, float InZoomFactor, const FSlateRect& InClippingRect, class FSlateWindowElementList& InDrawElements, class UEdGraph* InGraphObj) const override;

	virtual FLinearColor GetPinTypeColor(const FEdGraphPinType& PinType) const override;

	virtual void BreakNodeLinks(UEdGraphNode& TargetNode) const override;

	virtual void BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotifcation) const override;

	virtual void BreakSinglePinLink(UEdGraphPin* SourcePin, UEdGraphPin* TargetPin) const override;

	virtual UEdGraphPin* DropPinOnNode(UEdGraphNode* InTargetNode, const FName& InSourcePinName, const FEdGraphPinType& InSourcePinType, EEdGraphPinDirection InSourcePinDirection) const override;

	virtual bool SupportsDropPinOnNode(UEdGraphNode* InTargetNode, const FEdGraphPinType& InSourcePinType, EEdGraphPinDirection InSourcePinDirection, FText& OutErrorMessage) const override;

	virtual bool IsCacheVisualizationOutOfDate(int32 InVisualizationCacheID) const override;

	virtual int32 GetCurrentVisualizationCacheID() const override;

	virtual void ForceVisualizationCacheClear() const override;

	virtual void CreateDefaultNodesForGraph(UEdGraph& Graph) const override;

private:
	
	static int32 CurrentCacheRefreshID;
};
