#pragma once

#include "CoreMinimal.h"
#include "WingHandler.h"
#include "WingServer.h"
#include "WingFetcher.h"
#include "WingProperty.h"
#include "WingUtils.h"
#include "EdGraph/EdGraphPin.h"
#include "EdGraphSchema_K2.h"
#include "MaterialGraph/MaterialGraphSchema.h"
#include "GraphNode_SetDefaults.generated.h"


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

USTRUCT()
struct FSetNodeDefaultEntry
{
	GENERATED_BODY()

	UPROPERTY()
	FString Node;

	UPROPERTY()
	FString Name;

	UPROPERTY()
	FString Value;
};


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

public:
	UPROPERTY(EditAnywhere, meta=(Description="Target graph"))
	FString Graph;

	UPROPERTY(EditAnywhere, meta=(Description="Array of {node, name, value} objects"))
	FWingJsonArray Pins;

	virtual void Register() override
	{
		UWingServer::AddHandler(this,
			TEXT("Set the default value of input pins or material expression properties on nodes."));
	}
	// -----------------------------------------------------------------------
	// K2 graphs: set pin default values.
	// -----------------------------------------------------------------------

	void HandleK2Entry(const FSetNodeDefaultEntry& Entry, UEdGraph* GraphObj, const UEdGraphSchema_K2* K2Schema)
	{
		WingFetcher F(GraphObj);
		UEdGraphPin* Pin = F.Node(Entry.Node).Pin(Entry.Name).Cast<UEdGraphPin>();
		if (!Pin) return;

		UEdGraphNode* Node = Pin->GetOwningNode();

		if (Pin->Direction != EGPD_Input)
		{
			UWingServer::Printf(TEXT("error: %s is an output pin\n"), *WingUtils::FormatName(Pin));
			return;
		}

		FString UseDefaultValue;
		TObjectPtr<UObject> UseDefaultObject = nullptr;
		FText UseDefaultText;
		K2Schema->GetPinDefaultValuesFromString(Pin->PinType, Node, Entry.Value, UseDefaultValue, UseDefaultObject, UseDefaultText, false);
		FString Error = K2Schema->IsPinDefaultValid(Pin, UseDefaultValue, UseDefaultObject, UseDefaultText);
		if (!Error.IsEmpty())
		{
			UWingServer::Printf(TEXT("error: %s: %s\n"), *WingUtils::FormatName(Pin), *Error);
			return;
		}
		UWingServer::AddTouchedObject(Node);
		K2Schema->TrySetDefaultValue(*Pin, Entry.Value);
	}

	// -----------------------------------------------------------------------
	// Material graphs: set material expression properties.
	// -----------------------------------------------------------------------

	void HandleMaterialEntry(const FSetNodeDefaultEntry& Entry, UEdGraph* GraphObj)
	{
		WingFetcher F(GraphObj);
		UEdGraphNode* Node = F.Node(Entry.Node).Cast<UEdGraphNode>();
		if (!Node) return;

		TArray<FWingProperty> All = FWingProperty::GetDetailsMutable(Node, CPF_Edit);
		FWingProperty *P = WingUtils::FindOneWithExternalID(Entry.Name, All, TEXT("Property"));
		if (!P) return;

		UWingServer::AddTouchedObject(Node);

		if (!P->SetText(Entry.Value))
			return;
	}

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

	virtual void Handle() override
	{
		// Fetch the graph once.
		WingFetcher GraphFetcher;
		UEdGraph* GraphObj = GraphFetcher.Walk(Graph).Cast<UEdGraph>();
		if (!GraphObj) return;

		const UEdGraphSchema* Schema = GraphObj->GetSchema();
		const UEdGraphSchema_K2* K2Schema = Cast<UEdGraphSchema_K2>(Schema);
		const UMaterialGraphSchema* MGSchema = Cast<UMaterialGraphSchema>(Schema);

		if (!K2Schema && !MGSchema)
		{
			UWingServer::Printf(TEXT("error: unsupported graph schema %s\n"), *Schema->GetClass()->GetName());
			return;
		}

		for (const TSharedPtr<FJsonValue>& PinVal : Pins.Array)
		{
			FSetNodeDefaultEntry Entry;
			if (!FWingProperty::PopulateFromJson(FSetNodeDefaultEntry::StaticStruct(), &Entry, PinVal))
				continue;

			if (K2Schema)
				HandleK2Entry(Entry, GraphObj, K2Schema);
			else if (MGSchema)
				HandleMaterialEntry(Entry, GraphObj);
		}

		UWingServer::Printf(TEXT("Done.\n"));
	}
};
