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

#include "SSourcifyNode.h"
#include "AssetThumbnail.h"
#include "EdGraph_Sourcify.h"
#include "EdGraphNode_Sourcify.h"
#include "Math/MathFwd.h"
#include "Misc/EngineVersionComparison.h"
#include "SCommentBubble.h"
#include "SourcifyGraphStyle.h"
#include "SSourcifyGraph.h"
#include "Widgets/Images/SImage.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/Notifications/SErrorText.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/Text/SInlineEditableTextBlock.h"

#define LOCTEXT_NAMESPACE "SourcifyGraph"

using namespace Sourcify;

void SSourcifyNode::Construct(const FArguments& InArgs, UEdGraphNode_Sourcify* InNode)
{
	const int32 ThumbnailSize = 128;

	if (InNode->AllowsThumbnail())
	{
		if (InNode->UsesThumbnail())
		{
			// Create a thumbnail from the graph's thumbnail pool
			TSharedPtr<FAssetThumbnailPool> AssetThumbnailPool = InNode->GetSourcifyGraph()->GetAssetThumbnailPool();
			AssetThumbnail = MakeShareable(new FAssetThumbnail(InNode->GetAssetData(), ThumbnailSize, ThumbnailSize, AssetThumbnailPool));
		}
		else if (InNode->IsPackage() || InNode->IsCollapsed())
		{
			// Just make a generic thumbnail
			AssetThumbnail = MakeShareable(new FAssetThumbnail(InNode->GetAssetData(), ThumbnailSize, ThumbnailSize, NULL));
		}
	}

	GraphNode = InNode;
	InNode->SourcifyWidgetNode = this;
	SetCursor(EMouseCursor::CardinalCross);
	UpdateGraphNode();
}

// UpdateGraphNode is similar to the base, but adds the option to hide the thumbnail */
void SSourcifyNode::UpdateGraphNode()
{
	OutputPins.Empty();

	// Reset variables that are going to be exposed, in case we are refreshing an already setup node.
	RightNodeBox.Reset();
	LeftNodeBox.Reset();

	//
	//             ______________________
	//            |      TITLE AREA      |
	//            +-------+------+-------+
	//            | (>) L |      | R (>) |
	//            | (>) E |      | I (>) |
	//            | (>) F |      | G (>) |
	//            | (>) T |      | H (>) |
	//            |       |      | T (>) |
	//            |_______|______|_______|
	//
	TSharedPtr<SVerticalBox> MainVerticalBox;
	SetupErrorReporting();
	TSharedPtr<SErrorText> ErrorText;
	TSharedPtr<SNodeTitle> NodeTitle = SNew(SNodeTitle, GraphNode).StyleSet(&FSourcifyGraphStyle::Get());

	// Get node icon
	IconColor = FLinearColor::White;
	const FSlateBrush* IconBrush = nullptr;
	if (GraphNode != NULL && GraphNode->ShowPaletteIconOnNode())
	{
		IconBrush = GraphNode->GetIconAndTint(IconColor).GetOptionalIcon();
	}

	TSharedRef<SWidget> ThumbnailWidget = SNullWidget::NullWidget;
	UEdGraphNode_Sourcify* RefGraphNode = CastChecked<UEdGraphNode_Sourcify>(GraphNode);
	bool bIsADuplicate = RefGraphNode->IsADuplicate();

	FLinearColor OpacityColor = RefGraphNode->GetIsFiltered() ? FLinearColor(1.0, 1.0, 1.0, 0.4) : FLinearColor::White;

	if (AssetThumbnail.IsValid())
	{

		FAssetThumbnailConfig ThumbnailConfig;
		ThumbnailConfig.bAllowFadeIn = RefGraphNode->UsesThumbnail();
		ThumbnailConfig.bForceGenericThumbnail = !RefGraphNode->UsesThumbnail();
		ThumbnailConfig.AssetTypeColorOverride = FLinearColor::Transparent;

		ThumbnailWidget =
			SNew(SBox)
			.WidthOverride(AssetThumbnail->GetSize().X)
			.HeightOverride(AssetThumbnail->GetSize().Y)
			[
				AssetThumbnail->MakeThumbnailWidget(ThumbnailConfig)
			];
	}

	ContentScale.Bind(this, &SSourcifyNode::GetContentScale);
	GetOrAddSlot(ENodeZone::Center)
		.HAlign(HAlign_Center)
		.VAlign(VAlign_Center)
		[
			SAssignNew(MainVerticalBox, SVerticalBox)
				+ SVerticalBox::Slot()
				.AutoHeight()
				[
					SNew(SBorder)
						.ColorAndOpacity(OpacityColor)
						.BorderImage(FSourcifyGraphStyle::Get().GetBrush("Graph.ReferenceInfo.BodyBackground"))
						.Padding(0)
						[

							SNew(SBorder)
								.BorderImage(FSourcifyGraphStyle::Get().GetBrush("Graph.ReferenceInfo.BodyBorder"))
								.BorderBackgroundColor(this, &SSourcifyNode::GetNodeTitleColor)
								.Padding(0)
								[

									SNew(SBorder)
										.BorderImage(FSourcifyGraphStyle::Get().GetBrush("Graph.ReferenceInfo.Body"))
										.Padding(0)
										[

											SNew(SVerticalBox)
												.ToolTipText(this, &SSourcifyNode::GetNodeTooltip)

												+ SVerticalBox::Slot()
												.AutoHeight()
												.HAlign(HAlign_Fill)
												.VAlign(VAlign_Top)
												.Padding(0)
												[
													SNew(SBorder)
														.BorderImage(FSourcifyGraphStyle::Get().GetBrush("Graph.ReferenceInfo.ColorSpill"))
														.Padding(FMargin(10.0f, 4.0f, 6.0f, 4.0f))
														.BorderBackgroundColor(this, &SSourcifyNode::GetNodeTitleColor)
														[
															SNew(SHorizontalBox)
																+ SHorizontalBox::Slot()
																.VAlign(VAlign_Center)
																.Padding(FMargin(0.f, 0.f, 8.f, 0.f))
																.AutoWidth()
																[
																	SNew(SImage)
																		.Image(IconBrush)
																		.DesiredSizeOverride(FVector2D(24.0, 24.0))
																		.ColorAndOpacity(this, &SGraphNode::GetNodeTitleIconColor)
																]
																+ SHorizontalBox::Slot()
																.FillWidth(1.0)
																.VAlign(VAlign_Center)
																[
																	SNew(SVerticalBox)
																		+ SVerticalBox::Slot()
																		.AutoHeight()
																		.Padding(FMargin(0.f))
																		.VAlign(VAlign_Center)
																		[
																			SAssignNew(InlineEditableText, SInlineEditableTextBlock)
																				.Style(FSourcifyGraphStyle::Get(), "Graph.ReferenceInfo.NodeTitleInlineEditableText")
																				.Text(NodeTitle.Get(), &SNodeTitle::GetHeadTitle)
																				.OnVerifyTextChanged(this, &SSourcifyNode::OnVerifyNameTextChanged)
																				.OnTextCommitted(this, &SSourcifyNode::OnNameTextCommited)
																				.IsReadOnly(this, &SSourcifyNode::IsNameReadOnly)
																				.IsSelected(this, &SSourcifyNode::IsSelectedExclusively)
																		]
																	+ SVerticalBox::Slot()
																		.AutoHeight()
																		.Padding(FMargin(0.f))
																		[
																			NodeTitle.ToSharedRef()
																		]
																]

															+ SHorizontalBox::Slot()
																.AutoWidth()
																.HAlign(HAlign_Right)
																.VAlign(VAlign_Bottom)
																.Padding(FMargin(4.f, 0.f, 0.f, 1.f))
																[
																	SNew(SImage)
																		.Visibility(bIsADuplicate ? EVisibility::Visible : EVisibility::Hidden)
																		.ToolTipText(LOCTEXT("DuplicateAsset", "This asset is referenced multiple times. Only the first occurance shows its decendants."))
																		.DesiredSizeOverride(FVector2D(12.0, 12.0))
																		.Image(FSourcifyGraphStyle::Get().GetBrush("Graph.ReferenceInfo.Duplicate"))
																		.ColorAndOpacity(FAppStyle::Get().GetColor("Colors.Foreground"))
																]
														]
												]
											+ SVerticalBox::Slot()
												.AutoHeight()
												.Padding(1.0f)
												[
													// POPUP ERROR MESSAGE
													SAssignNew(ErrorText, SErrorText)
														.BackgroundColor(this, &SSourcifyNode::GetErrorColor)
														.ToolTipText(this, &SSourcifyNode::GetErrorMsgToolTip)
												]
												+ SVerticalBox::Slot()
												.AutoHeight()
												.HAlign(HAlign_Fill)
												.VAlign(VAlign_Top)
												[
													// NODE CONTENT AREA
													SNew(SBorder)
														.BorderImage(FAppStyle::GetBrush("NoBorder"))
														.HAlign(HAlign_Fill)
														.VAlign(VAlign_Fill)
														.Padding(FMargin(0, 3))
														[
															SNew(SHorizontalBox)
																+ SHorizontalBox::Slot()
																.AutoWidth()
																.VAlign(VAlign_Center)
																[
																	// LEFT
																	SNew(SBox)
																		.WidthOverride(40)
																		[
																			SAssignNew(LeftNodeBox, SVerticalBox)
																		]
																]

															+ SHorizontalBox::Slot()
																.VAlign(VAlign_Center)
																.HAlign(HAlign_Center)
																.FillWidth(1.0f)
																[
																	// Thumbnail
																	ThumbnailWidget
																]

																+ SHorizontalBox::Slot()
																.AutoWidth()
																.VAlign(VAlign_Center)
																[
																	// RIGHT
																	SNew(SBox)
																		.WidthOverride(40)
																		[
																			SAssignNew(RightNodeBox, SVerticalBox)
																		]
																]
														]
												]

										] // Inner Background 
								] // Outline Border
						] // Background 
				]
		];
	// Create comment bubble if comment text is valid
	GetNodeObj()->bCommentBubbleVisible = !GetNodeObj()->NodeComment.IsEmpty();
	if (GetNodeObj()->ShouldMakeCommentBubbleVisible() && GetNodeObj()->bCommentBubbleVisible)
	{
		TSharedPtr<SCommentBubble> CommentBubble;

		SAssignNew(CommentBubble, SCommentBubble)
			.GraphNode(GraphNode)
			.Text(this, &SGraphNode::GetNodeComment);

		GetOrAddSlot(ENodeZone::TopCenter)
#if UE_VERSION_NEWER_THAN(5, 5, 9)
			.SlotOffset2f(TAttribute<FVector2f>(CommentBubble.Get(), &SCommentBubble::GetOffset2f))
			.SlotSize2f(TAttribute<FVector2f>(CommentBubble.Get(), &SCommentBubble::GetSize2f))
#else
			.SlotOffset(TAttribute<FVector2D>(CommentBubble.Get(), &SCommentBubble::GetOffset))
			.SlotSize(TAttribute<FVector2D>(CommentBubble.Get(), &SCommentBubble::GetSize))
#endif
			.AllowScaling(TAttribute<bool>(CommentBubble.Get(), &SCommentBubble::IsScalingAllowed))
			.VAlign(VAlign_Top)
			[
				CommentBubble.ToSharedRef()
			];
	}

	ErrorReporting = ErrorText;
	ErrorReporting->SetError(ErrorMsg);
	CreateBelowWidgetControls(MainVerticalBox);

	CreatePinWidgets();
}

void SSourcifyNode::UpdateErrorInfo()
{
	if (UEdGraphNode_Sourcify* SourcifyGraphNode = Cast<UEdGraphNode_Sourcify>(GraphNode))
	{
		switch (SourcifyGraphNode->ValidateConversionSettings())
		{
		case NodeErrorStatus::None:
			ErrorMsg.Empty();
			ErrorColor = FLinearColor(0, 0, 0);
			break;

		case NodeErrorStatus::Warning:
			ErrorMsg = FString(TEXT("WARNING!"));
			ErrorColor = FAppStyle::GetColor("ErrorReporting.WarningBackgroundColor");
			break;

		case NodeErrorStatus::Fatal:
			ErrorMsg = FString(TEXT("ERROR!"));
			ErrorColor = FAppStyle::GetColor("ErrorReporting.BackgroundColor");
			break;

		default:
			check(false);
			break;
		}
	}

	if (ErrorReporting.IsValid())
	{
		ErrorReporting->SetError(ErrorMsg);
	}

	VisualWarningMsg = FString(TEXT("WARNING!"));
	VisualWarningColor = FAppStyle::GetColor("ErrorReporting.WarningBackgroundColor");
}

void SSourcifyNode::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
	SGraphNode::OnMouseEnter(MyGeometry, MouseEvent);
	
	if (UEdGraphNode_Sourcify* SourcifyGraphNode = Cast<UEdGraphNode_Sourcify>(GraphNode))
	{
		FAssetData AssetData = SourcifyGraphNode->GetAssetData();
		if (AssetData.IsValid())
		{
			FName NodePath(*AssetData.PackageName.ToString());
			if (UEdGraph_Sourcify* SourcifyGraph = SourcifyGraphNode->GetSourcifyGraph())
			{
				if (TSharedPtr<SSourcifyGraph> SourcifyGraphWidget = SourcifyGraph->SourcifyGraph.Pin())
				{
					SourcifyGraphWidget->OnGraphNodeMouseEnter(NodePath);
				}
			}
		}
	}
}

void SSourcifyNode::OnMouseLeave(const FPointerEvent& MouseEvent)
{
	SGraphNode::OnMouseLeave(MouseEvent);
	
	if (UEdGraphNode_Sourcify* SourcifyGraphNode = Cast<UEdGraphNode_Sourcify>(GraphNode))
	{
		if (UEdGraph_Sourcify* SourcifyGraph = SourcifyGraphNode->GetSourcifyGraph())
		{
			if (TSharedPtr<SSourcifyGraph> SourcifyGraphWidget = SourcifyGraph->SourcifyGraph.Pin())
			{
				SourcifyGraphWidget->OnGraphNodeMouseLeave();
			}
		}
	}
}

#undef LOCTEXT_NAMESPACE
