#pragma once

#include "CoreMinimal.h"
#include "WingServer.h"
#include "WingBasics.h"
#include "WingFetcher.h"
#include "WingWidgets.h"
#include "WingUtils.h"
#include "WidgetBlueprint.h"
#include "Blueprint/WidgetTree.h"
#include "Blueprint/UserWidget.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Components/PanelWidget.h"
#include "Widget_Add.generated.h"


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

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

public:
	UPROPERTY(EditAnywhere, meta=(Description="Widget blueprint to add the widget to"))
	FString BlueprintPath;

	UPROPERTY(EditAnywhere, meta=(Description="Widget type, from Widget_SearchTypes"))
	FString Type;

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

	UPROPERTY(EditAnywhere, meta=(Description="Parent widget name. If omitted, sets as root."))
	FString Parent;

	UPROPERTY(EditAnywhere, meta=(Description="Whether to expose the widget as a variable in the blueprint (default false)"))
	bool IsVariable = false;

	virtual void Register() override
	{
		UWingServer::AddHandler(this,
			TEXT("Add a widget to a Widget Blueprint's widget tree. "
			"Use Widget_SearchTypes to find available widget types."));
	}
	virtual void Handle() override
	{
		// Fetch the widget blueprint.
		WingFetcher F(WingOut::Stdout);
		UWidgetBlueprint* BP = F.Walk(BlueprintPath).Cast<UWidgetBlueprint>();
		if (!BP) return;

		// Resolve the widget type.
		WingWidgets WidgetMenu;
		TArray<WingWidgets::Type> TypeResults = WidgetMenu.Search(Type, 2, true);
		if (!WingUtils::CheckExactlyOneNamed(TypeResults.Num(), TEXT("Widget type"), Type, WingOut::Stdout)) return;

		// Load the widget's class.
		UClass* WidgetClass = TypeResults[0].Class.LoadSynchronous();
		if (!WidgetClass)
		{
			WingOut::Stdout.Printf(TEXT("ERROR: Failed to load widget class for '%s'\n"), *Type);
			return;
		}

		// Validate the proposed name.
		TSet<FName> InUse = WingUtils::GetAllInUseNames(BP);
		FName InternalID = WingUtils::CheckProposedName(Name, InUse, WingOut::Stdout);
		if (InternalID.IsNone()) return;
		
		// If a parent is specified, find it and verify it's a panel.
		UPanelWidget* ParentPanel = nullptr;
		TArray<UWidget*> AllWidgets;
		BP->WidgetTree->GetAllWidgets(AllWidgets);
		if (!Parent.IsEmpty())
		{
			UWidget* ParentWidget = WingUtils::FindOneWithExternalID(Parent, AllWidgets, TEXT("Widget"), WingOut::Stdout);
			if (!ParentWidget) return;
			if (!WingWidgets::CheckCanBeParent(ParentWidget, WingOut::Stdout)) return;
			ParentPanel = Cast<UPanelWidget>(ParentWidget);
		}
		else
		{
			if (BP->WidgetTree->RootWidget != nullptr)
			{
				WingOut::Stdout.Printf(TEXT("ERROR: Widget tree already has a root widget. Specify a Parent.\n"));
				return;
			}
		}

		// Create the widget.
		UWidget* NewWidget;
		if (WidgetClass->IsChildOf(UUserWidget::StaticClass()))
			NewWidget = CreateWidget<UUserWidget>(BP->WidgetTree, WidgetClass, InternalID);
		else
			NewWidget = NewObject<UWidget>(BP->WidgetTree, WidgetClass, InternalID, RF_Transactional);

		if (!NewWidget)
		{
			WingOut::Stdout.Printf(TEXT("ERROR: Failed to create widget\n"));
			return;
		}

		// Initialize defaults and set variable flag.
		NewWidget->CreatedFromPalette();
		NewWidget->bIsVariable = IsVariable;

		// Add to the tree.
		if (ParentPanel)
		{
			ParentPanel->AddChild(NewWidget);
		}
		else
		{
			BP->WidgetTree->RootWidget = NewWidget;
		}

		// Register a variable GUID for the new widget. UMG's compiler
		// ensures every widget in the tree is present in this map.
		// BP->OnVariableAdded(NewWidget->GetFName());

		WingOut::Stdout.Printf(TEXT("Created widget '%s' of type '%s'\n"), *Name, *Type);
	}
};
