#pragma once

#include "CoreMinimal.h"
#include "WingServer.h"
#include "WingBasics.h"
#include "WingFetcher.h"
#include "WingUtils.h"
#include "WingVariables.h"
#include "Engine/Blueprint.h"
#include "EdGraph/EdGraph.h"
#include "EdGraphSchema_K2.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "BlueprintGraph_Add.generated.h"


// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------

UCLASS()
class UWing_BlueprintGraph_Add : public UWingHandler
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere, meta=(Description="Blueprint name or package path"))
	FString BlueprintPath;

	UPROPERTY(EditAnywhere, meta=(Description="Name for the new graph"))
	FString Graph;

	UPROPERTY(EditAnywhere, meta=(Description="Type of graph: function or macro"))
	FString GraphType;

	UPROPERTY(EditAnywhere, meta=(Description="Variables"))
	FWingRestOfArgv Variables;

	virtual void Register() override
	{
		UWingServer::AddHandler(this,
			TEXT("Create a new function or macro graph in a Blueprint. "
			"Variables must be expressed as 'kind type name (flags) = default'. "
			"Kind can be input, output, or local."));
	}
	virtual void Handle() override
	{
		if (GraphType != TEXT("function") && GraphType != TEXT("macro"))
		{
			WingOut::Stdout.Printf(TEXT("ERROR: Invalid GraphType '%s'. Valid values: function, macro\n"), *GraphType);
			return;
		}

		WingFetcher F(WingOut::Stdout);
		UBlueprint* BP = F.Walk(BlueprintPath).Cast<UBlueprint>();
		if (!BP) return;

		// Check that this graph type is valid for this blueprint type
		if (BP->BlueprintType == BPTYPE_Interface)
		{
			WingOut::Stdout.Print(TEXT("ERROR: Cannot add graphs to interface blueprints.\n"));
			return;
		}
		if (BP->BlueprintType == BPTYPE_MacroLibrary && GraphType == TEXT("function"))
		{
			WingOut::Stdout.Print(TEXT("ERROR: Macro libraries cannot contain functions.\n"));
			return;
		}
		if (BP->BlueprintType == BPTYPE_FunctionLibrary && GraphType == TEXT("macro"))
		{
			WingOut::Stdout.Print(TEXT("ERROR: Function libraries cannot contain macros.\n"));
			return;
		}

		// Check graph name uniqueness and legality
		TSet<FName> InUse = WingUtils::GetAllInUseNames(BP);
		FName InternalID = WingUtils::CheckProposedName(Graph, InUse, WingOut::Stdout);
		if (InternalID.IsNone()) return;

		// Parse and validate variables before making changes
		WingVariables Vars;
		if (!Vars.Parse(Variables.Argv, false, WingOut::Stdout)) return;

		// Create the Graph
		UEdGraph* NewGraph = FBlueprintEditorUtils::CreateNewGraph(BP, InternalID,
				UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass());
		if (!NewGraph)
		{
			WingOut::Stdout.Print(TEXT("ERROR: Failed to create graph\n"));
			return;
		}

		if (GraphType == TEXT("function"))
		{
			FBlueprintEditorUtils::AddFunctionGraph(BP, NewGraph, /*bIsUserCreated=*/true, /*SignatureFromObject=*/static_cast<UClass*>(nullptr));
			FBlueprintEditorUtils::FindOrCreateFunctionResultNode(FBlueprintEditorUtils::GetEntryNode(NewGraph));
		}
		else if (GraphType == TEXT("macro"))
		{
			FBlueprintEditorUtils::AddMacroGraph(BP, NewGraph, /*bIsUserCreated=*/true, /*SignatureFromClass=*/nullptr);
		}

		// Create the variables on the new graph
		if (!Vars.SetBackingStore(NewGraph, WingOut::Stdout)) return;
		if (!Vars.Check(WingOut::Stdout)) return;
		if (!Vars.Create(WingOut::Stdout)) return;

		WingOut::Stdout.Printf(TEXT("Created %s graph: %s\n"), *GraphType, *WingUtils::FormatName(NewGraph));
	}
};
