#pragma once

#include "CoreMinimal.h"
#include "WingBasics.h"
#include "WingServer.h"
#include "WingFetcher.h"
#include "WingUtils.h"
#include "EdGraph/EdGraphPin.h"
#include "EdGraphSchema_K2.h"
#include "MaterialGraph/MaterialGraphSchema.h"
#include "GraphNode_SetDefault.generated.h"


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

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

	UPROPERTY(EditAnywhere, meta=(Description="Target node"))
	FString Node;

	UPROPERTY(EditAnywhere, meta=(Description="Pin or property name"))
	FString Name;

	UPROPERTY(EditAnywhere, meta=(Description="New default value"))
	FString Value;

	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 HandleK2(UEdGraph* GraphObj, const UEdGraphSchema_K2* K2Schema)
	{
		WingFetcher F(GraphObj, WingOut::Stdout);
		UWingGraphPinRef* PinRef = F.Node(Node).Pin(Name).Cast<UWingGraphPinRef>();
		if (!PinRef) return;
		UEdGraphPin* Pin = WingUtils::CheckGetPin(PinRef->Node, PinRef->PinName, WingOut::Stdout);
		if (!Pin) return;

		UEdGraphNode* FoundNode = Pin->GetOwningNode();

		if (Pin->Direction != EGPD_Input)
		{
			WingOut::Stdout.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, FoundNode, Value, UseDefaultValue, UseDefaultObject, UseDefaultText, false);
		FString Error = K2Schema->IsPinDefaultValid(Pin, UseDefaultValue, UseDefaultObject, UseDefaultText);
		if (!Error.IsEmpty())
		{
			WingOut::Stdout.Printf(TEXT("error: %s: %s\n"), *WingUtils::FormatName(Pin), *Error);
			return;
		}
		UWingServer::AddTouchedObject(FoundNode);
		K2Schema->TrySetDefaultValue(*Pin, Value);
	}

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

	void HandleMaterial(UEdGraph* GraphObj)
	{
		WingFetcher F(GraphObj, WingOut::Stdout);
		UEdGraphNode* FoundNode = F.Node(Node).Cast<UEdGraphNode>();
		if (!FoundNode) return;

		TArray<FWingProperty> All = FWingProperty::GetDetails(FoundNode, true);
		FWingProperty *P = WingUtils::FindOneWithExternalID(Name, All, TEXT("Property"), WingOut::Stdout);
		if (!P) return;

		UWingServer::AddTouchedObject(FoundNode);

		if (!P->SetText(Value, WingOut::Stdout))
			return;
	}

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

	virtual void Handle() override
	{
		// Fetch the graph once.
		WingFetcher GraphFetcher(WingOut::Stdout);
		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)
		{
			WingOut::Stdout.Printf(TEXT("error: unsupported graph schema %s\n"), *Schema->GetClass()->GetName());
			return;
		}

		if (K2Schema) HandleK2(GraphObj, K2Schema);
		else if (MGSchema) HandleMaterial(GraphObj);

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