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

#include "AssetEditor_ComboActionGraph.h"

#include "GraphEditorActions.h"
#include "Framework/Commands/GenericCommands.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "HAL/PlatformApplicationMisc.h"
#include "EdGraphUtilities.h"

#include "ComboInputEditor.h"
#include "ComboActionGraph.h"
#include "Nodes/ComboActionGraphNode.h"
#include "Ed/EdComboActionGraph.h"
#include "Ed/EdComboActionGraphEdge.h"
#include "Ed/EdComboActionGraphNode.h"
#include "Ed/FAssetEditorToolbarComboActionGraph.h"
#include "Ed/FComboActionGraphEditorCommands.h"
#include "ComboActionGraphSchema.h"
//#include "Helpers/ComboActionGraphHelpers.h"
//#include "Helpers/ComboActionSystemEditorBFC.h"
#include "Layout/AssetEditorTabs.h"
#include "Layout/ComboActionGraphLayoutStrategy.h"
#include "Layout/ComboActionForceDirectedSolveLayoutStrategy.h"
#include "Layout/ComboActionTreeSolveLayoutStrategy.h"
//#include "Popups/MDSPopup_GraphValidation.h"
#include "Search/ComboActionSearchUtils.h"
#include "Search/SComboActionSearch.h"
#include "Settings/ComboActionGraphEditorSettings.h"
#include "UObject/ObjectSaveContext.h"

#define LOCTEXT_NAMESPACE "AssetEditor_ComboActionGraph"

DEFINE_LOG_CATEGORY(LogAssetEditorComboActionGraph);

#pragma region ConstantNames

const FName ComboActionGraphEditorAppName = FName(TEXT("ComboActionGraphEditorApp"));
const FName FAssetEditorTabs_ComboActionGraph::ComboActionGraphPropertyID(TEXT("ComboActionGraphProperty"));
const FName FAssetEditorTabs_ComboActionGraph::ViewportID(TEXT("Viewport"));
const FName FAssetEditorTabs_ComboActionGraph::SearchToolbarID(TEXT("Search"));

#pragma endregion


FAssetEditor_ComboActionGraph::FAssetEditor_ComboActionGraph()
{
	this->EditingGraph = nullptr;
	this->ComboActionGraphEditorSettings = GetMutableDefault<UComboActionGraphEditorSettings>();
	this->OnPackageSavedDelegateHandle = UPackage::PackageSavedWithContextEvent.AddRaw(this, &FAssetEditor_ComboActionGraph::OnPackageSaved);
}

FAssetEditor_ComboActionGraph::~FAssetEditor_ComboActionGraph()
{
	this->EditingGraph = nullptr;
	UPackage::PackageSavedWithContextEvent.Remove(this->OnPackageSavedDelegateHandle);

	this->ToolbarBuilder.Reset();
}

void FAssetEditor_ComboActionGraph::OnPackageSaved(const FString &String, UPackage *Package, FObjectPostSaveContext ObjectPostSaveContext)
{
	this->RebuildComboActionGraph();
}

void FAssetEditor_ComboActionGraph::InitComboActionGraphAssetEditor(const EToolkitMode::Type Mode, const TSharedPtr<IToolkitHost>& InitToolkitHost, UComboActionGraph *Graph)
{
	this->EditingGraph = Graph;
	this->CreateEdGraph();

	if (!this->ToolbarBuilder.IsValid())
	{
		this->ToolbarBuilder = MakeShareable(new FAssetEditorToolbarComboActionGraph(SharedThis(this)));
	}

	this->BindCommands();

	this->CreateInternalWidgets();

	TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender);
	this->ToolbarBuilder->AddComboActionGraphToolbar(ToolbarExtender);

	// Layout
	const TSharedRef<FTabManager::FLayout> StandaloneDefaultLayout = FTabManager::NewLayout("Standalone_ComboActionGraphEditor_LayoutV0.3")
		->AddArea
		(
			FTabManager::NewPrimaryArea()->SetOrientation(EOrientation::Orient_Vertical)
			->Split
			(
				FTabManager::NewSplitter()->SetOrientation(EOrientation::Orient_Horizontal)->SetSizeCoefficient(0.9f)
				->Split
				(
					FTabManager::NewStack()
					->SetSizeCoefficient(3.f)
					->AddTab(FAssetEditorTabs_ComboActionGraph::ViewportID, ETabState::OpenedTab)->SetHideTabWell(true)
				)
				->Split
				(
					FTabManager::NewSplitter()->SetOrientation(EOrientation::Orient_Vertical)
					->Split
					(
					FTabManager::NewSplitter()->SetOrientation(EOrientation::Orient_Vertical)
						->Split
						(
							FTabManager::NewStack()
							->SetSizeCoefficient(0.9f)
							->AddTab(FAssetEditorTabs_ComboActionGraph::ComboActionGraphPropertyID, ETabState::OpenedTab)->SetHideTabWell(true)
						)

						->Split
						(
							FTabManager::NewStack()
							->SetSizeCoefficient(0.3f)
							->AddTab(FAssetEditorTabs_ComboActionGraph::SearchToolbarID, ETabState::OpenedTab)
						)
					)
				)
			)
		);
	
	const bool bCreateDefaultStandaloneMenu = true;
	const bool bCreateDefaultToolbar = true;
	FAssetEditorToolkit::InitAssetEditor
	(
		Mode,
		InitToolkitHost,
		ComboActionGraphEditorAppName,
		StandaloneDefaultLayout,
		bCreateDefaultStandaloneMenu,
		bCreateDefaultToolbar,
		EditingGraph,
		false
		);

	this->RegenerateMenusAndToolbars();
}

UComboActionGraphEditorSettings *FAssetEditor_ComboActionGraph::GetSettings() const
{
	return this->ComboActionGraphEditorSettings;
}

void FAssetEditor_ComboActionGraph::RegisterTabSpawners(const TSharedRef<FTabManager> &InTabManager)
{
	this->WorkspaceMenuCategory = InTabManager->AddLocalWorkspaceMenuCategory(LOCTEXT("WorkspaceMenu_ComboActionTreeEditor", "Combo Action Tree Editor"));
	auto WorkspaceMenuCategoryRef = this->WorkspaceMenuCategory.ToSharedRef();

	FAssetEditorToolkit::RegisterTabSpawners(InTabManager);

	InTabManager->RegisterTabSpawner(FAssetEditorTabs_ComboActionGraph::ViewportID, FOnSpawnTab::CreateSP(this, &FAssetEditor_ComboActionGraph::SpawnTab_Viewport))
		.SetDisplayName(LOCTEXT("GraphCanvasTab", "Viewport"))
		.SetGroup(WorkspaceMenuCategoryRef)
		.SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "GraphEditor.EventGraph_16x"));

	InTabManager->RegisterTabSpawner(FAssetEditorTabs_ComboActionGraph::ComboActionGraphPropertyID, FOnSpawnTab::CreateSP(this, &FAssetEditor_ComboActionGraph::SpawnTab_Details))
		.SetDisplayName(LOCTEXT("DetailsTab", "Property"))
		.SetGroup(WorkspaceMenuCategoryRef)
		.SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Details"));

	InTabManager->RegisterTabSpawner(FAssetEditorTabs_ComboActionGraph::SearchToolbarID, FOnSpawnTab::CreateSP(this, &FAssetEditor_ComboActionGraph::SpawnTab_Search))
		.SetDisplayName(LOCTEXT("SearchTab", "Search"))
		.SetGroup(WorkspaceMenuCategoryRef)
		.SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.FindResults"));
}

void FAssetEditor_ComboActionGraph::UnregisterTabSpawners(const TSharedRef<FTabManager> &InTabManager)
{
	FAssetEditorToolkit::UnregisterTabSpawners(InTabManager);

	InTabManager->UnregisterTabSpawner(FAssetEditorTabs_ComboActionGraph::ViewportID);
	InTabManager->UnregisterTabSpawner(FAssetEditorTabs_ComboActionGraph::ComboActionGraphPropertyID);
	InTabManager->UnregisterTabSpawner(FAssetEditorTabs_ComboActionGraph::SearchToolbarID);
}

bool FAssetEditor_ComboActionGraph::CloseWindow(EAssetEditorCloseReason InCloseReason)
{
	const bool bSatisfied = FAssetEditorToolkit::CloseWindow(InCloseReason);

	if (this->EditingGraph)
	{
		if (this->EditingGraph->EdGraph)
		{
			UEdComboActionGraph *EdGraph = Cast<UEdComboActionGraph>(this->EditingGraph->EdGraph);
			if (EdGraph->GetComboActionEditorPtr().HasSameObject(this))	EdGraph->ResetDialogueEditorPtr();
		}
	}

	return bSatisfied;
}

FName FAssetEditor_ComboActionGraph::GetToolkitFName() const
{
	return FName("FComboActionGraphEditor");
}

FText FAssetEditor_ComboActionGraph::GetBaseToolkitName() const
{
	return LOCTEXT("ComboActionGraphEditorAppLabel", "Combo Action Graph Editor");
}

FText FAssetEditor_ComboActionGraph::GetToolkitName() const
{
	const bool bDirtyState = this->EditingGraph->GetOutermost()->IsDirty();

	FFormatNamedArguments Args;
	Args.Add(TEXT("ComboActionGraphName"), FText::FromString(this->EditingGraph->GetName()));
	Args.Add(TEXT("DirtyState"), bDirtyState ? FText::FromString(TEXT("*")) : FText::GetEmpty());
	return FText::Format(LOCTEXT("ComboActionGraphEditorToolkitName", "{ComboActionGraphName}{DirtyState}"), Args);
}

FText FAssetEditor_ComboActionGraph::GetToolkitToolTipText() const
{
	return FAssetEditorToolkit::GetToolTipTextForObject(EditingGraph);
}

FLinearColor FAssetEditor_ComboActionGraph::GetWorldCentricTabColorScale() const
{
	return FLinearColor::Gray;
}

FString FAssetEditor_ComboActionGraph::GetWorldCentricTabPrefix() const
{
	return TEXT("ComboActionGraphEditor");
}

FString FAssetEditor_ComboActionGraph::GetDocumentationLink() const
{
	return TEXT("There should be a link to the documentation here someday.");
}

void FAssetEditor_ComboActionGraph::SaveAsset_Execute()
{
	if (this->EditingGraph != nullptr)
	{
		this->RebuildComboActionGraph();
	}

	FAssetEditorToolkit::SaveAsset_Execute();
}

void FAssetEditor_ComboActionGraph::UpdateToolbar()
{
	// TODO
}

void FAssetEditor_ComboActionGraph::RegisterToolbarTab(const TSharedRef<FTabManager> &InTabManager)
{
	FAssetEditorToolkit::RegisterTabSpawners(InTabManager);
}

void FAssetEditor_ComboActionGraph::AddReferencedObjects(FReferenceCollector &Collector)
{
	Collector.AddReferencedObject(this->EditingGraph);
	Collector.AddReferencedObject(this->EditingGraph->EdGraph);
}

FString FAssetEditor_ComboActionGraph::GetReferencerName() const
{
	return TEXT("FAssetEditor_ComboActionGraph");
}

void FAssetEditor_ComboActionGraph::SetDialogueBeingEdited(UComboActionGraph *NewDialogue)
{
	if (NewDialogue == nullptr || NewDialogue == this->EditingGraph)
	{
		return;
	}

	UComboActionGraph *Previous = this->EditingGraph;
	this->EditingGraph = NewDialogue;

	this->RemoveEditingObject(Previous);
	this->AddEditingObject(NewDialogue);
}

void FAssetEditor_ComboActionGraph::CreateInternalWidgets()
{
	this->ViewportWidget = this->CreateViewportWidget();

	FDetailsViewArgs Args;
	Args.bUpdatesFromSelection = false;
	Args.bLockable = false;
	Args.bAllowSearch = true;
	Args.NameAreaSettings = FDetailsViewArgs::HideNameArea;
	Args.bHideSelectionTip = false;
	Args.bShowObjectLabel = false;

	FPropertyEditorModule &PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
	this->PropertyWidget = PropertyModule.CreateDetailView(Args);
	this->PropertyWidget->SetObject(this->EditingGraph);
	
	this->PropertyWidget->OnFinishedChangingProperties().AddSP(this, &FAssetEditor_ComboActionGraph::OnFinishedChangingProperties);

	this->FindResultsView = SNew(SComboActionSearch, SharedThis(this));
}

TSharedRef<SGraphEditor> FAssetEditor_ComboActionGraph::CreateViewportWidget()
{
	const FComboInputEditorModule &Module = FModuleManager::GetModuleChecked<FComboInputEditorModule>("ComboInputEditor");
	FGraphAppearanceInfo AppearanceInfo;
	AppearanceInfo.CornerText = LOCTEXT("AppearanceCornerText_ComboActionGraph", "Combo Action Tree");
	AppearanceInfo.CornerImage = Module.Get().GetComboInputEditorStyleSet()->GetBrush(TEXT("MDSStyleSet.Graph.CornerImage"));
	AppearanceInfo.InstructionText = LOCTEXT("InstructionText_ComboActionGraph", "Place action nodes by right clicking.");

	this->CreateCommandList();

	SGraphEditor::FGraphEditorEvents InEvents;
	InEvents.OnSelectionChanged = SGraphEditor::FOnSelectionChanged::CreateSP(this, &FAssetEditor_ComboActionGraph::OnSelectedNodesChanged);
	InEvents.OnNodeDoubleClicked = FSingleNodeEvent::CreateSP(this, &FAssetEditor_ComboActionGraph::OnNodeDoubleClicked);

	return SNew(SGraphEditor)
		.AdditionalCommands(GraphEditorCommands)
		.IsEditable(true)
		.Appearance(AppearanceInfo)
		.GraphToEdit(EditingGraph->EdGraph)
		.GraphEvents(InEvents)
		.AutoExpandActionMenu(true)
		.ShowGraphStateOverlay(false);
}

void FAssetEditor_ComboActionGraph::BindCommands()
{
	this->ToolkitCommands->MapAction
	(
		FComboActionGraphEditorCommands::Get().AutoArrange,
		FExecuteAction::CreateSP(this, &FAssetEditor_ComboActionGraph::AutoArrange),
		FCanExecuteAction::CreateSP(this, &FAssetEditor_ComboActionGraph::CanAutoArrange)
	);

	this->ToolkitCommands->MapAction
	(
		FComboActionGraphEditorCommands::Get().ValidateGraph,
		FExecuteAction::CreateSP(this, &FAssetEditor_ComboActionGraph::ValidateGraph),
		FCanExecuteAction::CreateSP(this, &FAssetEditor_ComboActionGraph::CanValidateGraph)
	);
}

void FAssetEditor_ComboActionGraph::CreateEdGraph()
{
	if (this->EditingGraph->EdGraph == nullptr)
	{
		this->EditingGraph->EdGraph = CastChecked<UEdComboActionGraph>(FBlueprintEditorUtils::CreateNewGraph(EditingGraph, NAME_None, UEdComboActionGraph::StaticClass(), UComboActionGraphSchema::StaticClass()));
		this->EditingGraph->EdGraph->bAllowDeletion = false;

		// Give the schema a chance to fill out any required nodes (like the results node)
		const UEdGraphSchema *Schema = this->EditingGraph->EdGraph->GetSchema();
		Schema->CreateDefaultNodesForGraph(*this->EditingGraph->EdGraph);

		UEdComboActionGraph *ComboActionGraph = Cast<UEdComboActionGraph>(this->EditingGraph->EdGraph);
		UEdComboActionGraphNode *NewNode = ComboActionGraph->CreateIntermediateNode<UEdComboActionGraphNode>();
	
		NewNode->SetComboActionGraphNode(this->EditingGraph->StartNode);
		NewNode->CreateNewGuid();
		NewNode->PostPlacedNewNode();
		NewNode->AllocateDefaultPins();
		NewNode->AutowireNewNode(nullptr);

		NewNode->NodePosX = 0;
		NewNode->NodePosY = 0;

		NewNode->ComboActionGraphNode->SetFlags(EObjectFlags::RF_Transactional);
		NewNode->SetFlags(EObjectFlags::RF_Transactional);
		
		ComboActionGraph->RebuildComboActionGraph();
	}

	if (UEdComboActionGraph *EdComboActionGraph = Cast<UEdComboActionGraph>(this->EditingGraph->EdGraph))
	{
		EdComboActionGraph->SetDialogueEditorPtr(SharedThis(this));
		EdComboActionGraph->RebuildComboActionGraph();
	}
}

void FAssetEditor_ComboActionGraph::CreateCommandList()
{
	if (this->GraphEditorCommands.IsValid())
	{
		return;
	}

	this->GraphEditorCommands = MakeShareable(new FUICommandList);

	this->GraphEditorCommands->MapAction(FComboActionGraphEditorCommands::Get().AutoArrange,
		FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::AutoArrange),
		FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanAutoArrange));

	this->GraphEditorCommands->MapAction(FComboActionGraphEditorCommands::Get().ValidateGraph,
		FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::ValidateGraph),
		FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanValidateGraph));

	this->GraphEditorCommands->MapAction(FGenericCommands::Get().SelectAll,
		FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::SelectAllNodes),
		FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanSelectAllNodes)
	);

	this->GraphEditorCommands->MapAction(FGenericCommands::Get().Delete,
		FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::DeleteSelectedNodes),
		FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanDeleteNodes)
	);

	this->GraphEditorCommands->MapAction(FGenericCommands::Get().Copy,
		FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CopySelectedNodes),
		FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanCopyNodes)
	);

	this->GraphEditorCommands->MapAction(FGenericCommands::Get().Cut,
		FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CutSelectedNodes),
		FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanCutNodes)
	);

	this->GraphEditorCommands->MapAction(FGenericCommands::Get().Paste,
		FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::PasteNodes),
		FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanPasteNodes)
	);

	this->GraphEditorCommands->MapAction(FGenericCommands::Get().Duplicate,
		FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::DuplicateNodes),
		FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanDuplicateNodes)
	);

	this->GraphEditorCommands->MapAction(FGenericCommands::Get().Rename,
		FExecuteAction::CreateSP(this, &FAssetEditor_ComboActionGraph::OnRenameNode),
		FCanExecuteAction::CreateSP(this, &FAssetEditor_ComboActionGraph::CanRenameNodes)
	);
}

TSharedPtr<SGraphEditor> FAssetEditor_ComboActionGraph::GetCurrentGraphEditor() const
{
	return this->ViewportWidget;
}

FGraphPanelSelectionSet FAssetEditor_ComboActionGraph::GetSelectedNodes() const
{
	FGraphPanelSelectionSet CurrentSelection;
	TSharedPtr<SGraphEditor> FocusedGraphEd = this->GetCurrentGraphEditor();
	if (FocusedGraphEd.IsValid())
	{
		CurrentSelection = FocusedGraphEd->GetSelectedNodes();
	}

	return CurrentSelection;
}

void FAssetEditor_ComboActionGraph::RebuildComboActionGraph()
{
	if (this->EditingGraph == nullptr)
	{
		UE_LOG(LogAssetEditorComboActionGraph, Warning, TEXT("[RebuildComboActionGraph] EditingGraph is nullptr"));
		return;
	}

	UEdComboActionGraph *EdGraph = Cast<UEdComboActionGraph>(this->EditingGraph->EdGraph);
	check(EdGraph != nullptr);

	EdGraph->RebuildComboActionGraph();
}

void FAssetEditor_ComboActionGraph::SelectAllNodes()
{
	TSharedPtr<SGraphEditor> CurrentGraphEditor = this->GetCurrentGraphEditor();
	if (CurrentGraphEditor.IsValid())
	{
		CurrentGraphEditor->SelectAllNodes();
	}
}

void FAssetEditor_ComboActionGraph::DeleteSelectedNodes()
{
	TSharedPtr<SGraphEditor> CurrentGraphEditor = this->GetCurrentGraphEditor();
	if (!CurrentGraphEditor.IsValid())
	{
		return;
	}

	const FScopedTransaction Transaction(FGenericCommands::Get().Delete->GetDescription());

	CurrentGraphEditor->GetCurrentGraph()->Modify();

	const FGraphPanelSelectionSet SelectedNodes = CurrentGraphEditor->GetSelectedNodes();
	CurrentGraphEditor->ClearSelectionSet();

	for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt)
	{
		UEdGraphNode* EdNode = Cast<UEdGraphNode>(*NodeIt);
		if (EdNode == nullptr || !EdNode->CanUserDeleteNode())
			continue;;

		if (UEdComboActionGraphNode *EdNode_Node = Cast<UEdComboActionGraphNode>(EdNode))
		{
			EdNode_Node->Modify();

			const UEdGraphSchema *Schema = EdNode_Node->GetSchema();
			if (Schema != nullptr)
			{
				Schema->BreakNodeLinks(*EdNode_Node);
			}

			EdNode_Node->DestroyNode();
		}
		else
		{
			EdNode->Modify();
			EdNode->DestroyNode();
		}
	}

	// Update UI
	CurrentGraphEditor->NotifyGraphChanged();

	UEdGraph *EdGraph = CurrentGraphEditor->GetCurrentGraph();
	UObject *GraphOwner = EdGraph->GetOuter();
	if (GraphOwner)
	{
		GraphOwner->PostEditChange();
		GraphOwner->MarkPackageDirty();
	}

	this->RebuildComboActionGraph();
}

bool FAssetEditor_ComboActionGraph::CanDeleteNodes() const
{
	// If any of the nodes can be deleted then we should allow deleting
	const FGraphPanelSelectionSet SelectedNodes = this->GetSelectedNodes();
	for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter)
	{
		UEdGraphNode* Node = Cast<UEdGraphNode>(*SelectedIter);
		if (Node != nullptr && Node->CanUserDeleteNode())
		{
			return true;
		}
	}

	return false;
}

void FAssetEditor_ComboActionGraph::DeleteSelectedDuplicatableNodes()
{
	TSharedPtr<SGraphEditor> CurrentGraphEditor = this->GetCurrentGraphEditor();
	if (!CurrentGraphEditor.IsValid())
	{
		return;
	}

	const FGraphPanelSelectionSet OldSelectedNodes = CurrentGraphEditor->GetSelectedNodes();
	CurrentGraphEditor->ClearSelectionSet();

	for (FGraphPanelSelectionSet::TConstIterator SelectedIter(OldSelectedNodes); SelectedIter; ++SelectedIter)
	{
		UEdGraphNode *Node = Cast<UEdGraphNode>(*SelectedIter);
		if (Node && Node->CanDuplicateNode())
		{
			CurrentGraphEditor->SetNodeSelection(Node, true);
		}
	}

	// Delete the duplicatable nodes
	this->DeleteSelectedNodes();

	CurrentGraphEditor->ClearSelectionSet();

	for (FGraphPanelSelectionSet::TConstIterator SelectedIter(OldSelectedNodes); SelectedIter; ++SelectedIter)
	{
		if (UEdGraphNode* Node = Cast<UEdGraphNode>(*SelectedIter))
		{
			CurrentGraphEditor->SetNodeSelection(Node, true);
		}
	}
}

void FAssetEditor_ComboActionGraph::CutSelectedNodes()
{
	this->CopySelectedNodes();
	this->DeleteSelectedDuplicatableNodes();
}

void FAssetEditor_ComboActionGraph::CopySelectedNodes()
{
	// Export the selected nodes and place the text on the clipboard
	FGraphPanelSelectionSet SelectedNodes = this->GetSelectedNodes();

	FString ExportedText;

	for (FGraphPanelSelectionSet::TIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter)
	{
		UEdGraphNode *Node = Cast<UEdGraphNode>(*SelectedIter);
		if (Node == nullptr)
		{
			SelectedIter.RemoveCurrent();
			continue;
		}

		if (UEdComboActionGraphEdge *EdNode_Edge = Cast<UEdComboActionGraphEdge>(*SelectedIter))
		{
			UEdComboActionGraphNode *StartNode = EdNode_Edge->GetStartNode();
			UEdComboActionGraphNode *EndNode = EdNode_Edge->GetEndNode();

			if (!SelectedNodes.Contains(StartNode) || !SelectedNodes.Contains(EndNode))
			{
				SelectedIter.RemoveCurrent();
				continue;
			}
		}

		Node->PrepareForCopying();
	}

	FEdGraphUtilities::ExportNodesToText(SelectedNodes, ExportedText);
	FPlatformApplicationMisc::ClipboardCopy(*ExportedText);
}

bool FAssetEditor_ComboActionGraph::CanCopyNodes() const
{
	// If any of the nodes can be duplicated then we should allow copying
	const FGraphPanelSelectionSet SelectedNodes = this->GetSelectedNodes();
	for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter)
	{
		UEdGraphNode *Node = Cast<UEdGraphNode>(*SelectedIter);
		if (Node && Node->CanDuplicateNode() == false)
		{
			return false;
		}
	}

	return true;
}

void FAssetEditor_ComboActionGraph::PasteNodes()
{
	TSharedPtr<SGraphEditor> CurrentGraphEditor = this->GetCurrentGraphEditor();
	if (CurrentGraphEditor.IsValid())
	{
		this->PasteNodesHere(CurrentGraphEditor->GetPasteLocation());
	}
}

void FAssetEditor_ComboActionGraph::PasteNodesHere(const FVector2D &Location)
{
	// Find the graph editor with focus
	TSharedPtr<SGraphEditor> CurrentGraphEditor = this->GetCurrentGraphEditor();
	if (!CurrentGraphEditor.IsValid())
	{
		return;
	}
	// Select the newly pasted stuff
	UEdGraph *EdGraph = CurrentGraphEditor->GetCurrentGraph();

	{
		const FScopedTransaction Transaction(FGenericCommands::Get().Paste->GetDescription());
		EdGraph->Modify();

		// Clear the selection set (newly pasted stuff will be selected)
		CurrentGraphEditor->ClearSelectionSet();

		// Grab the text to paste from the clipboard.
		FString TextToImport;
		FPlatformApplicationMisc::ClipboardPaste(TextToImport);

		// Import the nodes
		TSet<UEdGraphNode*> PastedNodes;
		FEdGraphUtilities::ImportNodesFromText(EdGraph, TextToImport, PastedNodes);

		//Average position of nodes so we can move them while still maintaining relative distances to each other
		FVector2D AvgNodePosition(0.0f, 0.0f);

		for (TSet<UEdGraphNode*>::TIterator It(PastedNodes); It; ++It)
		{
			UEdGraphNode* Node = *It;
			AvgNodePosition.X += Node->NodePosX;
			AvgNodePosition.Y += Node->NodePosY;
		}

		float InvNumNodes = 1.0f / float(PastedNodes.Num());
		AvgNodePosition.X *= InvNumNodes;
		AvgNodePosition.Y *= InvNumNodes;

		// 0 is always Start Node!
		int32 SharedIndex = EditingGraph->GetAllNodes().Num();

		for (TSet<UEdGraphNode*>::TIterator It(PastedNodes); It; ++It)
		{
			UEdGraphNode* Node = *It;
			CurrentGraphEditor->SetNodeSelection(Node, true);

			Node->NodePosX = (Node->NodePosX - AvgNodePosition.X) + Location.X;
			Node->NodePosY = (Node->NodePosY - AvgNodePosition.Y) + Location.Y;

			Node->SnapToGrid(16);

			// Give new node a different Guid from the old one
			Node->CreateNewGuid();

			if (UEdComboActionGraphNode *MounteaNode = Cast<UEdComboActionGraphNode>(Node))
			{
				if (MounteaNode->ComboActionGraphNode)
				{
					MounteaNode->ComboActionGraphNode->OnPasted();
					//MounteaNode->SetDialogueNodeIndex(SharedIndex);
				}

				SharedIndex++;
			}
		}
	}

	// Update UI
	CurrentGraphEditor->NotifyGraphChanged();

	UObject *GraphOwner = EdGraph->GetOuter();
	if (GraphOwner)
	{
		GraphOwner->PostEditChange();
		GraphOwner->MarkPackageDirty();
	}

	this->RebuildComboActionGraph();
}

bool FAssetEditor_ComboActionGraph::CanPasteNodes() const
{
	const FGraphPanelSelectionSet SelectedNodes = this->GetSelectedNodes();
	for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter)
	{
		const UEdComboActionGraphNode *Node = Cast<UEdComboActionGraphNode>(*SelectedIter);
		if (Node && Node->CanUserPasteNodes() == false)
		{
			return false;
		}
	}
	
	TSharedPtr<SGraphEditor> CurrentGraphEditor = this->GetCurrentGraphEditor();
	if (!CurrentGraphEditor.IsValid())
	{
		return false;
	}

	FString ClipboardContent;
	FPlatformApplicationMisc::ClipboardPaste(ClipboardContent);

	return FEdGraphUtilities::CanImportNodesFromText(CurrentGraphEditor->GetCurrentGraph(), ClipboardContent);
}

void FAssetEditor_ComboActionGraph::AutoArrange()
{
	UEdComboActionGraph *EdGraph = Cast<UEdComboActionGraph>(this->EditingGraph->EdGraph);
	check(EdGraph != nullptr);

	const FScopedTransaction Transaction(LOCTEXT("ComboActionGraphEditorAutoArrange", "Combo Action Graph Editor: Auto Arrange all Nodes"));

	EdGraph->Modify(true);

	UComboActionGraphLayoutStrategy *LayoutStrategy = nullptr;
	switch (this->ComboActionGraphEditorSettings->GetAutoLayoutStrategy())
	{
		case EComboActionAutoLayoutStrategyType::Tree:
			LayoutStrategy = NewObject<UComboActionGraphLayoutStrategy>(EdGraph, UComboActionTreeSolveLayoutStrategy::StaticClass());
			break;
		case EComboActionAutoLayoutStrategyType::ForceDirected:
			LayoutStrategy = NewObject<UComboActionGraphLayoutStrategy>(EdGraph, UComboActionForceDirectedSolveLayoutStrategy::StaticClass());
			break;
		default:
			break;
	}

	if (LayoutStrategy != nullptr)
	{
		LayoutStrategy->Layout(EdGraph);
		LayoutStrategy->ConditionalBeginDestroy();
	}
	else
	{
		UE_LOG(LogAssetEditorComboActionGraph, Error, TEXT("[AutoArrange] LayoutStrategy is null."));
	}
}

bool FAssetEditor_ComboActionGraph::CanAutoArrange() const
{
	return this->EditingGraph != nullptr && Cast<UEdComboActionGraph>(this->EditingGraph->EdGraph) != nullptr;
}

void FAssetEditor_ComboActionGraph::ValidateGraph()
{
	if (this->ValidationWindow.IsValid())
	{
		this->ValidationWindow->RequestDestroyWindow();
	}
	
	UEdComboActionGraph *EdGraph = Cast<UEdComboActionGraph>(this->EditingGraph->EdGraph);
	check(EdGraph != nullptr);

	const FScopedTransaction Transaction(LOCTEXT("ComboActionGraphEditorValidateGraph", "Combo Action Graph Editor: Validate Graph."));

	UComboActionGraph *ComboActionGraph = EdGraph->GetComboActionGraph();
	check(ComboActionGraph != nullptr);
	
	this->RebuildComboActionGraph();
	
	TArray<FText> ValidationMessages;
	if (ComboActionGraph->ValidateGraph(ValidationMessages, true) == false)
	{
		//ValidationWindow = MDSPopup_GraphValidation::Open(ValidationMessages);
	}
	else
	{
		ValidationMessages.Empty();
		//ValidationWindow = MDSPopup_GraphValidation::Open(ValidationMessages);
	}
}

void FAssetEditor_ComboActionGraph::OnRenameNode()
{
	TSharedPtr<SGraphEditor> CurrentGraphEditor = this->GetCurrentGraphEditor();
	if (CurrentGraphEditor.IsValid())
	{
		const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes();
		for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt)
		{
			UEdGraphNode *SelectedNode = Cast<UEdGraphNode>(*NodeIt);
			if (SelectedNode != nullptr && SelectedNode->bCanRenameNode)
			{
				CurrentGraphEditor->IsNodeTitleVisible(SelectedNode, true);
				break;
			}
		}
	}
}

bool FAssetEditor_ComboActionGraph::CanRenameNodes() const
{
	check(this->GetSettings() != nullptr);
	return this->GetSettings()->AllowRenameNodes() == true && this->GetSelectedNodes().Num() == 1;
}

void FAssetEditor_ComboActionGraph::OnSelectedNodesChanged(const TSet<UObject*> &NewSelection)
{
	TArray<UObject*> Selection;

	for (UObject* SelectionEntry : NewSelection)
	{
		Selection.Add(SelectionEntry);
	}
	
	if (Selection.Num() == 1)
	{
		// When just one node is selected, add it to PropertyWidget
		this->PropertyWidget->SetObjects(Selection);
		this->PropertyWidget->ShowAllAdvancedProperties();
		
		//UComboActionEditorBFC::TriggerPreviewRefresh(Selection);
	}
	else
	{
		this->PropertyWidget->SetObject(this->EditingGraph);
	}

	this->RebuildComboActionGraph();
}

void FAssetEditor_ComboActionGraph::OnNodeDoubleClicked(UEdGraphNode *Node)
{
	this->GraphEditorCommands->TryExecuteAction(FGenericCommands::Get().Rename.ToSharedRef());
}

void FAssetEditor_ComboActionGraph::OnFinishedChangingProperties(const FPropertyChangedEvent& PropertyChangedEvent)
{
	if (this->EditingGraph == nullptr)
		return;

	this->EditingGraph->EdGraph->GetSchema()->ForceVisualizationCacheClear();

	this->RebuildComboActionGraph();
}

TSharedRef<SDockTab> FAssetEditor_ComboActionGraph::SpawnTab_Viewport(const FSpawnTabArgs &Args)
{
	check(Args.GetTabId() == FAssetEditorTabs_ComboActionGraph::ViewportID);

	TSharedRef<SDockTab> SpawnedTab = SNew(SDockTab)
		.Label(LOCTEXT("ViewportTab_Title", "Viewport"));

	if (this->ViewportWidget.IsValid())
	{
		SpawnedTab->SetContent(this->ViewportWidget.ToSharedRef());
	}

	return SpawnedTab;
}

TSharedRef<SDockTab> FAssetEditor_ComboActionGraph::SpawnTab_Details(const FSpawnTabArgs& Args)
{
	check(Args.GetTabId() == FAssetEditorTabs_ComboActionGraph::ComboActionGraphPropertyID);

	auto DockTab = SNew(SDockTab)
		.Label(LOCTEXT("Details_Title", "Property"))
		[
			this->PropertyWidget.ToSharedRef()
		];

	DockTab->SetTabIcon(FAppStyle::GetBrush("LevelEditor.Tabs.Details"));
	return DockTab;
}

TSharedRef<SDockTab> FAssetEditor_ComboActionGraph::SpawnTab_Search(const FSpawnTabArgs &Args)
{
	check(Args.GetTabId() == FAssetEditorTabs_ComboActionGraph::SearchToolbarID);

	auto DockTab = SNew(SDockTab)
		.Label(LOCTEXT("Search_Title", "Search"))
		[
			this->FindResultsView.ToSharedRef()
		];

	DockTab->SetTabIcon(FAppStyle::GetBrush("Kismet.Tabs.FindResults"));
	return DockTab;
}

#undef LOCTEXT_NAMESPACE

