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

#include "EdGraphNode_Sourcify.h"
#include "AssetToolsModule.h"
#include "EdGraph/EdGraphPin.h"
#include "EdGraph_Sourcify.h"
#include "GenericPlatform/GenericPlatformFile.h"
#include "HAL/PlatformFileManager.h"
#include "IAssetTools.h"
#include "IAssetTypeActions.h"
#include "Misc/PackageName.h"
#include "SSourcifyGraph.h"
#include "Styling/AppStyle.h"
#include "SSourcifyNode.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(EdGraphNode_Sourcify)

using namespace Sourcify;

#define LOCTEXT_NAMESPACE "SourcifyGraph"

//////////////////////////////////////////////////////////////////////////
// UEdGraphNode_Sourcify

UEdGraphNode_Sourcify::UEdGraphNode_Sourcify()
{
	DependencyPin = NULL;
	ReferencerPin = NULL;
	bIsCollapsed = false;
	bIsPackage = false;
	bIsPrimaryAsset = false;
	bUsesThumbnail = false;
	bAllowThumbnail = true;
	AssetTypeColor = FLinearColor(0.55f, 0.55f, 0.55f);
	bIsFiltered = false;
	bIsOverflow = false;
}

void UEdGraphNode_Sourcify::SetupReferenceNode(const Sourcify::DependencyNode::ReferenceInfo& InInfo, const FIntPoint& NodeLoc, const TArray<FAssetIdentifier>& NewIdentifiers, const FAssetData& InAssetData, bool bInAllowThumbnail, bool bInIsADuplicate)
{
	check(NewIdentifiers.Num() > 0);

	Info = InInfo;
	// Initialize the node's ready state with the reference info so that the node's properly report any errors to the graph UI upon initialization
	Info.Node->UpdateReadyState();

	NodePosX = NodeLoc.X;
	NodePosY = NodeLoc.Y;

	Identifiers = NewIdentifiers;
	const FAssetIdentifier& First = NewIdentifiers[0];
	FString MainAssetName = InAssetData.AssetName.ToString();
	FString AssetTypeName = InAssetData.AssetClassPath.GetAssetName().ToString();

	FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>(TEXT("AssetTools"));	
	if (UClass* AssetClass = InAssetData.GetClass())
	{
		TWeakPtr<IAssetTypeActions> AssetTypeActions = AssetToolsModule.Get().GetAssetTypeActionsForClass(InAssetData.GetClass());
		if(AssetTypeActions.IsValid())
		{
			AssetTypeColor = AssetTypeActions.Pin()->GetTypeColor();
		}
	}
	AssetBrush = FSlateIcon("EditorStyle", FName( *("ClassIcon." + AssetTypeName)));

	bIsCollapsed = false;
	bIsPackage = true;
	bAllowThumbnail = bInAllowThumbnail;
	bIsADuplicate = bInIsADuplicate;

	FPrimaryAssetId PrimaryAssetID = NewIdentifiers[0].GetPrimaryAssetId();
	if (PrimaryAssetID.IsValid())  // Management References (PrimaryAssetIDs)
	{
		static FText ManagerText = LOCTEXT("ReferenceManager", "Manager");
		MainAssetName = PrimaryAssetID.PrimaryAssetType.ToString() + TEXT(":") + PrimaryAssetID.PrimaryAssetName.ToString();
		AssetTypeName = ManagerText.ToString();
		bIsPackage = false;
		bIsPrimaryAsset = true;
	}
	else if (First.IsValue()) // Searchable Names (GamePlay Tags, Data Table Row Handle)
	{
		MainAssetName = First.ValueName.ToString();
		AssetTypeName = First.ObjectName.ToString();
		static const FName NAME_DataTable(TEXT("DataTable"));
		static const FText InDataTableText = LOCTEXT("InDataTable", "In DataTable");
		if (InAssetData.AssetClassPath.GetAssetName() == NAME_DataTable)
		{
			AssetTypeName = InDataTableText.ToString() + TEXT(" ") + AssetTypeName;
		}

		bIsPackage = false;
	}
	else if (First.IsPackage() && !InAssetData.IsValid()) 
	{
		const FString PackageNameStr = Identifiers[0].PackageName.ToString();
		if ( PackageNameStr.StartsWith(TEXT("/Script")) )// C++ Packages (/Script Code)
		{
			MainAssetName = PackageNameStr.RightChop(8);
			AssetTypeName = TEXT("Script");
		}
	}

	if (NewIdentifiers.Num() == 1 )
	{
		static const FName NAME_ActorLabel(TEXT("ActorLabel"));
		InAssetData.GetTagValue(NAME_ActorLabel, MainAssetName); 

		// append the type so it shows up on the extra line
		NodeTitle = FText::FromString(FString::Printf(TEXT("%s\n%s"), *MainAssetName, *AssetTypeName));

		if (bIsPackage)
		{
			NodeComment = First.PackageName.ToString();
		}
	}
	else
	{
		NodeTitle = FText::Format(LOCTEXT("ReferenceNodeMultiplePackagesComment", "{0} and {1} others"), FText::FromString(MainAssetName), FText::AsNumber(NewIdentifiers.Num() - 1));
	}
	
	UEdGraph_Sourcify* SourcifyGraph = Cast<UEdGraph_Sourcify>(GetGraph());
	ensure(SourcifyGraph);
	if (SourcifyGraph)
	{
		SourcifyGraph->GetOnNodeTypeChangedDelegate().AddUObject(this, &UEdGraphNode_Sourcify::OnNodeTypeChanged);
	}

	CacheAssetData(InAssetData);
	AllocateDefaultPins();
}

void UEdGraphNode_Sourcify::SetReferenceNodeCollapsed(const FIntPoint& NodeLoc, int32 InNumReferencesExceedingMax, const TArray<FAssetIdentifier>& NewIdentifiers)
{
	NodePosX = NodeLoc.X;
	NodePosY = NodeLoc.Y;

	Identifiers = NewIdentifiers;
	bIsCollapsed = true;
	bUsesThumbnail = false;
	bIsOverflow = true;
	AssetBrush = FSlateIcon(FAppStyle::GetAppStyleSetName(), "Icons.WarningWithColor");

	NodeTitle = FText::Format( LOCTEXT("ReferenceNodeCollapsedTitle", "{0} Collapsed nodes"), FText::AsNumber(InNumReferencesExceedingMax));
	CacheAssetData(FAssetData());
	AllocateDefaultPins();
}

void UEdGraphNode_Sourcify::AddReferencer(UEdGraphNode_Sourcify* ReferencerNode)
{
	UEdGraphPin* ReferencerDependencyPin = ReferencerNode->GetDependencyPin();

	if ( ensure(ReferencerDependencyPin) )
	{
		ReferencerDependencyPin->bHidden = false;
		ReferencerPin->bHidden = false;
		ReferencerPin->MakeLinkTo(ReferencerDependencyPin);
	}
}

void UEdGraphNode_Sourcify::OnNodeTypeChanged(Sourcify::DependencyNode* InDependencyNodePtr)
{
	check(InDependencyNodePtr);

	// For now just recheck this node's entire readiness state regardless of what changed on the incoming dependency node 
	if (InDependencyNodePtr != Info.Node.get())
	{
		Info.Node->UpdateReadyState();
		SourcifyWidgetNode->UpdateErrorInfo();
	}
}

FAssetIdentifier UEdGraphNode_Sourcify::GetIdentifier() const
{
	if (Identifiers.Num() > 0)
	{
		return Identifiers[0];
	}

	return FAssetIdentifier();
}

void UEdGraphNode_Sourcify::GetAllIdentifiers(TArray<FAssetIdentifier>& OutIdentifiers) const
{
	OutIdentifiers.Append(Identifiers);
}

void UEdGraphNode_Sourcify::GetAllPackageNames(TArray<FName>& OutPackageNames) const
{
	for (const FAssetIdentifier& AssetId : Identifiers)
	{
		if (AssetId.IsPackage())
		{
			OutPackageNames.AddUnique(AssetId.PackageName);
		}
	}
}

UEdGraph_Sourcify* UEdGraphNode_Sourcify::GetSourcifyGraph() const
{
	return Cast<UEdGraph_Sourcify>( GetGraph() );
}

FText UEdGraphNode_Sourcify::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
	return NodeTitle;
}

FLinearColor UEdGraphNode_Sourcify::GetNodeTitleColor() const
{
	if (bIsPrimaryAsset)
	{
		return FLinearColor(0.2f, 0.8f, 0.2f);
	}
	else if (bIsPackage)
	{
		return AssetTypeColor;
	}
	else if (bIsCollapsed)
	{
		return FLinearColor(0.55f, 0.55f, 0.55f);
	}
	else 
	{
		return FLinearColor(0.0f, 0.55f, 0.62f);
	}
}

FText UEdGraphNode_Sourcify::GetTooltipText() const
{
	return FText::FromString(Info.Node->ConversionSettings.LastInputValidityErrorText.c_str());
}

FSlateIcon UEdGraphNode_Sourcify::GetIconAndTint(FLinearColor& OutColor) const
{
	OutColor = bIsOverflow ? FLinearColor::White : AssetTypeColor;
	return AssetBrush;
}

void UEdGraphNode_Sourcify::AllocateDefaultPins()
{
	ReferencerPin = CreatePin( EEdGraphPinDirection::EGPD_Input, NAME_None, NAME_None);
	DependencyPin = CreatePin( EEdGraphPinDirection::EGPD_Output, NAME_None, NAME_None);

	ReferencerPin->bHidden = true;
	FName PassiveName = ::GetName(EDependencyPinCategory::LinkEndPassive);
	ReferencerPin->PinType.PinCategory = PassiveName;
	DependencyPin->bHidden = true;
	DependencyPin->PinType.PinCategory = PassiveName;

	ReferencerPin->PinToolTip = Info.Description.c_str();
}

UObject* UEdGraphNode_Sourcify::GetJumpTargetForDoubleClick() const
{
	if (Identifiers.Num() > 0 )
	{
		//GetSourcifyGraph()->SetGraphRoot(Identifiers, FIntPoint(NodePosX, NodePosY));
		GetSourcifyGraph()->RebuildGraph();
	}
	return NULL;
}

UEdGraphPin* UEdGraphNode_Sourcify::GetDependencyPin()
{
	return DependencyPin;
}

UEdGraphPin* UEdGraphNode_Sourcify::GetReferencerPin()
{
	return ReferencerPin;
}

const Sourcify::DependencyNode::ReferenceInfo& UEdGraphNode_Sourcify::GetInfo() const
{
	return Info;
}

NodeErrorStatus UEdGraphNode_Sourcify::ValidateConversionSettings() const
{
	// Check conversion settings are correct. This info is self-contained in each node
	return Info.Node->IsReadyForOutput();
}

void UEdGraphNode_Sourcify::UnRegisterDelegates()
{
	UEdGraph_Sourcify* SourcifyGraph = Cast<UEdGraph_Sourcify>(GetGraph());
	if (SourcifyGraph)
	{
		SourcifyGraph->GetOnNodeTypeChangedDelegate().RemoveAll(this);
	}
}	

void UEdGraphNode_Sourcify::CacheAssetData(const FAssetData& AssetData)
{
	if ( AssetData.IsValid() && IsPackage() )
	{
		bUsesThumbnail = true;
		CachedAssetData = AssetData;
	}
	else
	{
		CachedAssetData = FAssetData();
		bUsesThumbnail = false;

		if (Identifiers.Num() == 1 )
		{
			const FString PackageNameStr = Identifiers[0].PackageName.ToString();
			if ( FPackageName::IsValidLongPackageName(PackageNameStr, true) )
			{
				if ( PackageNameStr.StartsWith(TEXT("/Script")) )
				{
					// Used Only in the UI for the Thumbnail
					CachedAssetData.AssetClassPath = FTopLevelAssetPath(TEXT("/EdGraphNode_Reference"), TEXT("Code"));
				}
				else
				{
					const FString PotentiallyMapFilename = FPackageName::LongPackageNameToFilename(PackageNameStr, FPackageName::GetMapPackageExtension());
					const bool bIsMapPackage = FPlatformFileManager::Get().GetPlatformFile().FileExists(*PotentiallyMapFilename);
					if ( bIsMapPackage )
					{
						// Used Only in the UI for the Thumbnail
						CachedAssetData.AssetClassPath = TEXT("/Script/Engine.World");
					}
				}
			}
		}
		else
		{
			CachedAssetData.AssetClassPath = FTopLevelAssetPath(TEXT("/EdGraphNode_Reference"), TEXT("Multiple Nodes"));
		}
	}

}

FAssetData UEdGraphNode_Sourcify::GetAssetData() const
{
	return CachedAssetData;
}

bool UEdGraphNode_Sourcify::AllowsThumbnail() const
{
	return bAllowThumbnail;
}

bool UEdGraphNode_Sourcify::UsesThumbnail() const
{
	return bUsesThumbnail;
}

bool UEdGraphNode_Sourcify::IsPackage() const
{
	return bIsPackage;
}

bool UEdGraphNode_Sourcify::IsCollapsed() const
{
	return bIsCollapsed;
}

void UEdGraphNode_Sourcify::SetIsFiltered(bool bInFiltered)
{
	bIsFiltered = bInFiltered;	
}

bool UEdGraphNode_Sourcify::GetIsFiltered() const
{
	return bIsFiltered;
}

#undef LOCTEXT_NAMESPACE

