// Copyright (c) 2023 Codemongous LLC. All Rights Reserved.
#pragma once

#include "CoreMinimal.h"
#include "AssetRegistry/AssetData.h"
#include "AssetRegistry/AssetIdentifier.h"
#include "EdGraph/EdGraph.h"
#include <memory>
#include "SourcifyCore.h"
#include "UObject/ObjectMacros.h"

#include "EdGraph_Sourcify.generated.h"

using std::shared_ptr;

class FAssetThumbnailPool;
class UEdGraphNode_Sourcify;
class SSourcifyGraph;
class UReferenceViewerSettings;
enum class EDependencyPinCategory;

DECLARE_MULTICAST_DELEGATE_OneParam(FOnNodeTypeChanged, Sourcify::DependencyNode*);

/*
*  Holds asset information for building reference graph
*/ 
struct FSourcifyNodeInfo
{
	Sourcify::DependencyNode::ReferenceInfo ReferenceInfo;
	
	FAssetData AssetData;

	// immediate children (references or dependencies)
	TArray<TPair<FSourcifyNodeInfo, EDependencyPinCategory>> Children;

	// this node's parent references (how it got included)
	TArray<FSourcifyNodeInfo> Parents;

	// Which direction.  Referencers are left (other assets that depend on me), Dependencies are right (other assets I depend on)
	bool bReferencers;

	int32 OverflowCount;

	// Denote when all children have been manually expanded and the breadth limit should be ignored
	bool bExpandAllChildren;

	FSourcifyNodeInfo() = default;
	FSourcifyNodeInfo(Sourcify::DependencyNode::ReferenceInfo InInfo, bool InbReferencers);

	bool IsFirstParent(const FAssetIdentifier& InParentId) const;

	bool IsADuplicate() const;

	// The Provision Size, or vertical spacing required for layout, for a given parent.  
	// At the time of writing, the intent is only the first node manifestation of 
	// an asset will have its children shown
	int32 ProvisionSize(const FAssetIdentifier& InParentId) const;

	// how many nodes worth of children require vertical spacing 
	int32 ChildProvisionSize;

	// Whether or not this nodeinfo passed the current filters 
	bool PassedFilters;

	bool operator==(const FSourcifyNodeInfo& Other) const
	{
		return ReferenceInfo.Node == Other.ReferenceInfo.Node;
	}

};

UCLASS()
class UEdGraph_Sourcify : public UEdGraph
{
	GENERATED_BODY()

public:
	UEdGraph_Sourcify();
	// UObject implementation
	virtual void BeginDestroy() override;
	// End UObject implementation

	void SetGraphRoot(shared_ptr<Sourcify::DependencyNode> GraphRootNode, const FIntPoint& GraphRootOrigin = FIntPoint(ForceInitToZero));

	/** Accessor for the thumbnail pool in this graph */
	const TSharedPtr<class FAssetThumbnailPool>& GetAssetThumbnailPool() const;

	/** Force the graph to rebuild */
	class UEdGraphNode_Sourcify* RebuildGraph();

	/* Returns a set of unique asset types as UClass* */
	const TSet<FTopLevelAssetPath>& GetAssetTypes() const { return CurrentClasses; }

	bool AreAllNodesReadyToBeSourcified() const;

	FOnNodeTypeChanged& GetOnNodeTypeChangedDelegate() { return OnNodeTypeChanged; }

private:
	void SetSourcifyGraph(TSharedPtr<SSourcifyGraph> InViewer);
	UEdGraphNode_Sourcify* ConstructNodes(shared_ptr<Sourcify::DependencyNode> InGraphRoot, const FIntPoint& GraphRootOrigin);

	UEdGraphNode_Sourcify* CreateReferenceNode();

	/* Generates a NodeInfo structure then used to generate and layout the graph nodes */
	void RecursivelyPopulateNodeInfos(bool bReferencers, Sourcify::DependencyNode::ReferenceInfo Node, FSourcifyNodeInfo& NodeInfo, int32 CurrentDepth, int32 MaxDepth);

	/* Searches for the AssetData for the list of packages derived from the AssetReferences  */
	void GatherAssetData(TMap<FAssetIdentifier, FSourcifyNodeInfo>& InNodeInfos);

	/* Uses the NodeInfos map to generate and layout the graph nodes */
	UEdGraphNode_Sourcify* RecursivelyCreateNodes(
		bool bInReferencers, 
		const FIntPoint& InNodeLoc, 
		const FAssetIdentifier& InParentId, 
		UEdGraphNode_Sourcify* InParentNode, 
		const FSourcifyNodeInfo& InNodeInfo, 
		int32 InCurrentDepth, 
		int32 InMaxDepth, 
		bool bIsRoot = false
	);

	/** Removes all nodes from the graph */
	void RemoveAllNodes();

	UEdGraphNode_Sourcify* BuildNodes();

	UFUNCTION()
	void OnBPCompiled(UBlueprint* BP) const;

private:
	/** Pool for maintaining and rendering thumbnails */
	TSharedPtr<FAssetThumbnailPool> AssetThumbnailPool;

	/** Editor for this pool */
	TWeakPtr<SSourcifyGraph> SourcifyGraph;

	shared_ptr<Sourcify::DependencyNode> CurrentDependencyRootNode;
	FIntPoint CurrentGraphRootOrigin;

	int32 MaxSearchBreadth;

	/** Stores if the breadth limit was reached on the last re-filter*/
	bool bBreadthLimitReached;

	/** Current collection filter. NAME_None for no filter */
	FName CurrentCollectionFilter;

	/** A set of the unique class types referenced */
	TSet<FTopLevelAssetPath> CurrentClasses;

	/* This is a convenience toggle to switch between the old & new methods for computing & displaying the graph */
	bool bUseNodeInfos;

	FSourcifyNodeInfo RootNodeInfo;

	/** Broadcast every time a node's type is changed */
	FOnNodeTypeChanged OnNodeTypeChanged;

	/** Guard to let our on-compiled callback know to not retrigger a compile */
	mutable bool bIsGraphRefreshPending = false;

	friend SSourcifyGraph;
	friend class SSourcifyNode;
};
