#include "HyperTwistUX/HyperTwistHigherDimensionalHUDWidget.h"

#include "Blueprint/WidgetTree.h"
#include "Components/Border.h"
#include "Components/Button.h"
#include "Components/ButtonSlot.h"
#include "Components/HorizontalBox.h"
#include "Components/HorizontalBoxSlot.h"
#include "Components/Overlay.h"
#include "Components/OverlaySlot.h"
#include "Components/SizeBox.h"
#include "Components/TextBlock.h"
#include "Components/VerticalBox.h"
#include "Components/VerticalBoxSlot.h"

namespace HyperTwistHigherDimensionalHudWidgetInternal
{
	const FLinearColor PanelColor(0.010f, 0.023f, 0.039f, 0.965f);
	const FLinearColor CardColor(0.030f, 0.064f, 0.090f, 0.985f);
	const FLinearColor CyanAccent(0.12f, 0.95f, 1.0f, 1.0f);
	const FLinearColor OrangeAccent(1.0f, 0.60f, 0.22f, 1.0f);
	const FLinearColor TextColor(0.93f, 0.97f, 1.0f, 1.0f);
	const FLinearColor MutedColor(0.62f, 0.72f, 0.80f, 1.0f);
	const FLinearColor WarmColor(1.0f, 0.73f, 0.37f, 1.0f);
}

UHyperTwistHigherDimensionalHUDWidget::
	UHyperTwistHigherDimensionalHUDWidget(
		const FObjectInitializer& ObjectInitializer
	)
	: Super(ObjectInitializer)
{
	SetIsFocusable(false);
}

TSharedRef<SWidget> UHyperTwistHigherDimensionalHUDWidget::RebuildWidget()
{
	Initialize();
	EnsureWidgetTreeBuilt();
	return Super::RebuildWidget();
}

bool UHyperTwistHigherDimensionalHUDWidget::PrepareHudSurface()
{
	EnsureWidgetTreeBuilt();
	return IsHudSurfaceReady();
}

bool UHyperTwistHigherDimensionalHUDWidget::IsHudSurfaceReady() const
{
	return WidgetTree != nullptr
		&& WidgetTree->RootWidget != nullptr
		&& TitleText != nullptr
		&& StateText != nullptr
		&& SelectionText != nullptr
		&& ProjectionText != nullptr
		&& PositiveTurnButton != nullptr
		&& NegativeTurnButton != nullptr;
}

void UHyperTwistHigherDimensionalHUDWidget::ConfigureSurface(
	const bool bMagic120Cell,
	const bool bPositive5DFace,
	const bool bAutoRotating,
	const FString& Title,
	const FString& StateSummary,
	const FString& SelectionSummary,
	const FString& ProjectionSummary,
	const FString& LastAction,
	const FString& PersistenceSummary
)
{
	using namespace HyperTwistHigherDimensionalHudWidgetInternal;
	EnsureWidgetTreeBuilt();
	const FLinearColor Accent = bMagic120Cell ? CyanAccent : OrangeAccent;

	if (EyebrowText != nullptr)
	{
		EyebrowText->SetText(FText::FromString(
			bMagic120Cell
				? TEXT("EXACT 120-CELL LAB")
				: TEXT("EXACT FIVE-DIMENSIONAL LAB")));
		EyebrowText->SetColorAndOpacity(FSlateColor(Accent));
	}
	if (TitleText != nullptr)
	{
		TitleText->SetText(FText::FromString(Title));
	}
	if (StateText != nullptr)
	{
		StateText->SetText(FText::FromString(StateSummary));
		StateText->SetColorAndOpacity(FSlateColor(
			StateSummary.Contains(TEXT("solved"), ESearchCase::IgnoreCase)
				? Accent
				: TextColor));
	}
	if (SelectionText != nullptr)
	{
		SelectionText->SetText(FText::FromString(SelectionSummary));
	}
	if (ProjectionText != nullptr)
	{
		ProjectionText->SetText(FText::FromString(ProjectionSummary));
	}
	if (LastActionText != nullptr)
	{
		LastActionText->SetText(FText::FromString(
			LastAction.IsEmpty()
				? TEXT("Last action: ready")
				: FString::Printf(TEXT("Last action: %s"), *LastAction)));
	}
	if (PersistenceText != nullptr)
	{
		PersistenceText->SetText(FText::FromString(
			FString::Printf(TEXT("Session: %s"), *PersistenceSummary)));
	}

	SetButtonLabel(
		PreviousPrimaryButton,
		bMagic120Cell ? TEXT("Cell -") : TEXT("Face -"));
	SetButtonLabel(
		NextPrimaryButton,
		bMagic120Cell ? TEXT("Cell +") : TEXT("Face +"));
	SetButtonLabel(
		PreviousSecondaryButton,
		bMagic120Cell ? TEXT("Twist -") : TEXT("Layer -"));
	SetButtonLabel(
		NextSecondaryButton,
		bMagic120Cell ? TEXT("Twist +") : TEXT("Layer +"));
	SetButtonLabel(
		NegativeTurnButton,
		bMagic120Cell ? TEXT("Inverse") : TEXT("-90 degrees"));
	SetButtonLabel(
		PositiveTurnButton,
		bMagic120Cell ? TEXT("Forward") : TEXT("+90 degrees"));
	SetButtonLabel(
		FaceSideButton,
		bPositive5DFace ? TEXT("Face side: +") : TEXT("Face side: -"));
	SetButtonLabel(
		AutoRotateButton,
		bAutoRotating ? TEXT("Pause rotation") : TEXT("Resume rotation"));

	if (TertiaryActionRow != nullptr)
	{
		TertiaryActionRow->SetVisibility(
			bMagic120Cell
				? ESlateVisibility::Collapsed
				: ESlateVisibility::Visible);
	}
	if (FaceSideButton != nullptr)
	{
		FaceSideButton->SetVisibility(
			bMagic120Cell
				? ESlateVisibility::Collapsed
				: ESlateVisibility::Visible);
	}
	if (GuidanceText != nullptr)
	{
		GuidanceText->SetText(FText::FromString(
			bMagic120Cell
				? TEXT("Choose one of 120 cells and one of its 62 legal face, edge, or vertex axes, then apply the exact forward or inverse permutation.")
				: TEXT("Choose a signed face, any non-empty layer mask, and an orthogonal rotation plane, then apply an exact quarter turn.")));
	}
}

void UHyperTwistHigherDimensionalHUDWidget::EnsureWidgetTreeBuilt()
{
	using namespace HyperTwistHigherDimensionalHudWidgetInternal;
	if (WidgetTree == nullptr || WidgetTree->RootWidget != nullptr)
	{
		return;
	}

	UOverlay* Root = WidgetTree->ConstructWidget<UOverlay>(
		UOverlay::StaticClass(),
		TEXT("HigherDimensionalHudRoot"));
	Root->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
	WidgetTree->RootWidget = Root;

	USizeBox* PanelSize = WidgetTree->ConstructWidget<USizeBox>(
		USizeBox::StaticClass(),
		TEXT("HigherDimensionalHudPanelSize"));
	PanelSize->SetWidthOverride(570.0f);
	if (UOverlaySlot* PanelSlot = Root->AddChildToOverlay(PanelSize))
	{
		PanelSlot->SetHorizontalAlignment(HAlign_Left);
		PanelSlot->SetVerticalAlignment(VAlign_Top);
		PanelSlot->SetPadding(FMargin(20.0f));
	}

	UBorder* Panel = WidgetTree->ConstructWidget<UBorder>(
		UBorder::StaticClass(),
		TEXT("HigherDimensionalHudPanel"));
	Panel->SetPadding(FMargin(20.0f));
	Panel->SetBrushColor(PanelColor);
	PanelSize->AddChild(Panel);

	UVerticalBox* Content = WidgetTree->ConstructWidget<UVerticalBox>(
		UVerticalBox::StaticClass(),
		TEXT("HigherDimensionalHudContent"));
	Panel->AddChild(Content);

	EyebrowText = MakeText(
		TEXT("HIGHER-DIMENSIONAL PUZZLE LAB"),
		11,
		CyanAccent,
		TEXT("HigherDimensionalHudEyebrow"));
	Content->AddChildToVerticalBox(EyebrowText);
	TitleText = MakeText(
		TEXT("Higher-Dimensional Puzzle"),
		27,
		TextColor,
		TEXT("HigherDimensionalHudTitle"));
	Content->AddChildToVerticalBox(TitleText);
	StateText = MakeText(
		TEXT("Exact state ready"),
		14,
		CyanAccent,
		TEXT("HigherDimensionalHudState"));
	if (UVerticalBoxSlot* StateSlot = Content->AddChildToVerticalBox(StateText))
	{
		StateSlot->SetPadding(FMargin(0.0f, 8.0f, 0.0f, 0.0f));
	}
	SelectionText = MakeText(
		TEXT("Selection ready"),
		13,
		TextColor,
		TEXT("HigherDimensionalHudSelection"));
	Content->AddChildToVerticalBox(SelectionText);
	ProjectionText = MakeText(
		TEXT("Projection ready"),
		12,
		MutedColor,
		TEXT("HigherDimensionalHudProjection"));
	Content->AddChildToVerticalBox(ProjectionText);
	LastActionText = MakeText(
		TEXT("Last action: ready"),
		12,
		MutedColor,
		TEXT("HigherDimensionalHudLastAction"));
	Content->AddChildToVerticalBox(LastActionText);
	PersistenceText = MakeText(
		TEXT("Session: not loaded"),
		12,
		WarmColor,
		TEXT("HigherDimensionalHudPersistence"));
	Content->AddChildToVerticalBox(PersistenceText);

	UHorizontalBox* PrimaryRow = WidgetTree->ConstructWidget<UHorizontalBox>(
		UHorizontalBox::StaticClass(),
		TEXT("HigherDimensionalHudPrimaryActions"));
	if (UVerticalBoxSlot* RowSlot = Content->AddChildToVerticalBox(PrimaryRow))
	{
		RowSlot->SetPadding(FMargin(0.0f, 14.0f, 0.0f, 4.0f));
	}
	PreviousPrimaryButton = AddButton(
		PrimaryRow,
		TEXT("Primary -"),
		TEXT("HigherDimensionalHudPreviousPrimary"));
	NextPrimaryButton = AddButton(
		PrimaryRow,
		TEXT("Primary +"),
		TEXT("HigherDimensionalHudNextPrimary"));
	PreviousSecondaryButton = AddButton(
		PrimaryRow,
		TEXT("Secondary -"),
		TEXT("HigherDimensionalHudPreviousSecondary"));
	NextSecondaryButton = AddButton(
		PrimaryRow,
		TEXT("Secondary +"),
		TEXT("HigherDimensionalHudNextSecondary"));
	PreviousPrimaryButton->OnClicked.AddDynamic(
		this,
		&UHyperTwistHigherDimensionalHUDWidget::HandlePreviousPrimary);
	NextPrimaryButton->OnClicked.AddDynamic(
		this,
		&UHyperTwistHigherDimensionalHUDWidget::HandleNextPrimary);
	PreviousSecondaryButton->OnClicked.AddDynamic(
		this,
		&UHyperTwistHigherDimensionalHUDWidget::HandlePreviousSecondary);
	NextSecondaryButton->OnClicked.AddDynamic(
		this,
		&UHyperTwistHigherDimensionalHUDWidget::HandleNextSecondary);

	TertiaryActionRow = WidgetTree->ConstructWidget<UHorizontalBox>(
		UHorizontalBox::StaticClass(),
		TEXT("HigherDimensionalHudTertiaryActions"));
	if (UVerticalBoxSlot* RowSlot =
			Content->AddChildToVerticalBox(TertiaryActionRow))
	{
		RowSlot->SetPadding(FMargin(0.0f, 0.0f, 0.0f, 4.0f));
	}
	PreviousTertiaryButton = AddButton(
		TertiaryActionRow,
		TEXT("Plane -"),
		TEXT("HigherDimensionalHudPreviousTertiary"));
	NextTertiaryButton = AddButton(
		TertiaryActionRow,
		TEXT("Plane +"),
		TEXT("HigherDimensionalHudNextTertiary"));
	FaceSideButton = AddButton(
		TertiaryActionRow,
		TEXT("Face side: +"),
		TEXT("HigherDimensionalHudFaceSide"));
	PreviousTertiaryButton->OnClicked.AddDynamic(
		this,
		&UHyperTwistHigherDimensionalHUDWidget::HandlePreviousTertiary);
	NextTertiaryButton->OnClicked.AddDynamic(
		this,
		&UHyperTwistHigherDimensionalHUDWidget::HandleNextTertiary);
	FaceSideButton->OnClicked.AddDynamic(
		this,
		&UHyperTwistHigherDimensionalHUDWidget::HandleToggleFaceSide);

	UHorizontalBox* TurnRow = WidgetTree->ConstructWidget<UHorizontalBox>(
		UHorizontalBox::StaticClass(),
		TEXT("HigherDimensionalHudTurnActions"));
	if (UVerticalBoxSlot* RowSlot = Content->AddChildToVerticalBox(TurnRow))
	{
		RowSlot->SetPadding(FMargin(0.0f, 4.0f, 0.0f, 4.0f));
	}
	NegativeTurnButton = AddButton(
		TurnRow,
		TEXT("Inverse"),
		TEXT("HigherDimensionalHudNegativeTurn"));
	PositiveTurnButton = AddButton(
		TurnRow,
		TEXT("Forward"),
		TEXT("HigherDimensionalHudPositiveTurn"),
		true);
	NegativeTurnButton->OnClicked.AddDynamic(
		this,
		&UHyperTwistHigherDimensionalHUDWidget::HandleNegativeTurn);
	PositiveTurnButton->OnClicked.AddDynamic(
		this,
		&UHyperTwistHigherDimensionalHUDWidget::HandlePositiveTurn);

	UHorizontalBox* ProjectionRow = WidgetTree->ConstructWidget<UHorizontalBox>(
		UHorizontalBox::StaticClass(),
		TEXT("HigherDimensionalHudProjectionActions"));
	if (UVerticalBoxSlot* RowSlot = Content->AddChildToVerticalBox(ProjectionRow))
	{
		RowSlot->SetPadding(FMargin(0.0f, 8.0f, 0.0f, 4.0f));
	}
	UButton* PreviousLayerButton = AddButton(
		ProjectionRow,
		TEXT("View layer -"),
		TEXT("HigherDimensionalHudPreviousProjectionLayer"));
	UButton* NextLayerButton = AddButton(
		ProjectionRow,
		TEXT("View layer +"),
		TEXT("HigherDimensionalHudNextProjectionLayer"));
	AutoRotateButton = AddButton(
		ProjectionRow,
		TEXT("Pause rotation"),
		TEXT("HigherDimensionalHudAutoRotate"));
	PreviousLayerButton->OnClicked.AddDynamic(
		this,
		&UHyperTwistHigherDimensionalHUDWidget::HandlePreviousProjectionLayer);
	NextLayerButton->OnClicked.AddDynamic(
		this,
		&UHyperTwistHigherDimensionalHUDWidget::HandleNextProjectionLayer);
	AutoRotateButton->OnClicked.AddDynamic(
		this,
		&UHyperTwistHigherDimensionalHUDWidget::HandleToggleAutoRotate);

	UHorizontalBox* SessionRow = WidgetTree->ConstructWidget<UHorizontalBox>(
		UHorizontalBox::StaticClass(),
		TEXT("HigherDimensionalHudSessionActions"));
	if (UVerticalBoxSlot* RowSlot = Content->AddChildToVerticalBox(SessionRow))
	{
		RowSlot->SetPadding(FMargin(0.0f, 8.0f, 0.0f, 4.0f));
	}
	UButton* ScrambleButton = AddButton(
		SessionRow,
		TEXT("Scramble"),
		TEXT("HigherDimensionalHudScramble"),
		true);
	UButton* ResetButton = AddButton(
		SessionRow,
		TEXT("Reset"),
		TEXT("HigherDimensionalHudReset"));
	UButton* SaveButton = AddButton(
		SessionRow,
		TEXT("Save"),
		TEXT("HigherDimensionalHudSave"));
	UButton* LoadButton = AddButton(
		SessionRow,
		TEXT("Load"),
		TEXT("HigherDimensionalHudLoad"));
	ScrambleButton->OnClicked.AddDynamic(
		this,
		&UHyperTwistHigherDimensionalHUDWidget::HandleScramble);
	ResetButton->OnClicked.AddDynamic(
		this,
		&UHyperTwistHigherDimensionalHUDWidget::HandleReset);
	SaveButton->OnClicked.AddDynamic(
		this,
		&UHyperTwistHigherDimensionalHUDWidget::HandleSave);
	LoadButton->OnClicked.AddDynamic(
		this,
		&UHyperTwistHigherDimensionalHUDWidget::HandleLoad);

	UHorizontalBox* UtilityRow = WidgetTree->ConstructWidget<UHorizontalBox>(
		UHorizontalBox::StaticClass(),
		TEXT("HigherDimensionalHudUtilityActions"));
	if (UVerticalBoxSlot* RowSlot = Content->AddChildToVerticalBox(UtilityRow))
	{
		RowSlot->SetPadding(FMargin(0.0f, 4.0f, 0.0f, 8.0f));
	}
	UButton* CoachButton = AddButton(
		UtilityRow,
		TEXT("Open coach"),
		TEXT("HigherDimensionalHudCoach"));
	UButton* MenuButton = AddButton(
		UtilityRow,
		TEXT("Menu and settings"),
		TEXT("HigherDimensionalHudMenu"));
	CoachButton->OnClicked.AddDynamic(
		this,
		&UHyperTwistHigherDimensionalHUDWidget::HandleCoach);
	MenuButton->OnClicked.AddDynamic(
		this,
		&UHyperTwistHigherDimensionalHUDWidget::HandleMenu);

	GuidanceText = MakeText(
		TEXT("Select a legal move and apply it."),
		11,
		MutedColor,
		TEXT("HigherDimensionalHudGuidance"));
	Content->AddChildToVerticalBox(GuidanceText);
	UTextBlock* HelpText = MakeText(
		TEXT("Arrows select  |  , / . turn  |  Wheel zoom  |  Shift+RMB or MMB orbit  |  Esc menu  |  F3 coach"),
		10,
		CyanAccent);
	if (UVerticalBoxSlot* HelpSlot = Content->AddChildToVerticalBox(HelpText))
	{
		HelpSlot->SetPadding(FMargin(0.0f, 8.0f, 0.0f, 0.0f));
	}
}

UTextBlock* UHyperTwistHigherDimensionalHUDWidget::MakeText(
	const FString& Text,
	const int32 FontSize,
	const FLinearColor& Color,
	const FName& Name
)
{
	UTextBlock* TextBlock = WidgetTree->ConstructWidget<UTextBlock>(
		UTextBlock::StaticClass(),
		Name);
	TextBlock->SetText(FText::FromString(Text));
	TextBlock->SetAutoWrapText(true);
	TextBlock->SetColorAndOpacity(FSlateColor(Color));
	FSlateFontInfo Font = TextBlock->GetFont();
	Font.Size = FontSize;
	TextBlock->SetFont(Font);
	return TextBlock;
}

UButton* UHyperTwistHigherDimensionalHUDWidget::AddButton(
	UHorizontalBox* Parent,
	const FString& Label,
	const FName& Name,
	const bool bPrimary
)
{
	using namespace HyperTwistHigherDimensionalHudWidgetInternal;
	UButton* Button = WidgetTree->ConstructWidget<UButton>(
		UButton::StaticClass(),
		Name);
	Button->SetBackgroundColor(bPrimary ? CyanAccent : CardColor);
	UTextBlock* LabelText = MakeText(
		Label,
		11,
		bPrimary ? FLinearColor(0.01f, 0.04f, 0.05f, 1.0f) : TextColor);
	LabelText->SetJustification(ETextJustify::Center);
	Button->AddChild(LabelText);
	if (UButtonSlot* ContentSlot = Cast<UButtonSlot>(LabelText->Slot))
	{
		ContentSlot->SetPadding(FMargin(9.0f, 7.0f));
		ContentSlot->SetHorizontalAlignment(HAlign_Center);
	}
	if (UHorizontalBoxSlot* ButtonSlot = Parent->AddChildToHorizontalBox(Button))
	{
		ButtonSlot->SetPadding(FMargin(2.0f));
		ButtonSlot->SetSize(FSlateChildSize(ESlateSizeRule::Fill));
	}
	return Button;
}

void UHyperTwistHigherDimensionalHUDWidget::SetButtonLabel(
	UButton* Button,
	const FString& Label
)
{
	if (Button != nullptr)
	{
		if (UTextBlock* LabelText = Cast<UTextBlock>(Button->GetContent()))
		{
			LabelText->SetText(FText::FromString(Label));
		}
	}
}

void UHyperTwistHigherDimensionalHUDWidget::HandlePreviousPrimary()
{
	OnPreviousPrimaryRequested.Broadcast();
}

void UHyperTwistHigherDimensionalHUDWidget::HandleNextPrimary()
{
	OnNextPrimaryRequested.Broadcast();
}

void UHyperTwistHigherDimensionalHUDWidget::HandlePreviousSecondary()
{
	OnPreviousSecondaryRequested.Broadcast();
}

void UHyperTwistHigherDimensionalHUDWidget::HandleNextSecondary()
{
	OnNextSecondaryRequested.Broadcast();
}

void UHyperTwistHigherDimensionalHUDWidget::HandlePreviousTertiary()
{
	OnPreviousTertiaryRequested.Broadcast();
}

void UHyperTwistHigherDimensionalHUDWidget::HandleNextTertiary()
{
	OnNextTertiaryRequested.Broadcast();
}

void UHyperTwistHigherDimensionalHUDWidget::HandleToggleFaceSide()
{
	OnToggleFaceSideRequested.Broadcast();
}

void UHyperTwistHigherDimensionalHUDWidget::HandleNegativeTurn()
{
	OnNegativeTurnRequested.Broadcast();
}

void UHyperTwistHigherDimensionalHUDWidget::HandlePositiveTurn()
{
	OnPositiveTurnRequested.Broadcast();
}

void UHyperTwistHigherDimensionalHUDWidget::HandlePreviousProjectionLayer()
{
	OnPreviousProjectionLayerRequested.Broadcast();
}

void UHyperTwistHigherDimensionalHUDWidget::HandleNextProjectionLayer()
{
	OnNextProjectionLayerRequested.Broadcast();
}

void UHyperTwistHigherDimensionalHUDWidget::HandleToggleAutoRotate()
{
	OnToggleAutoRotateRequested.Broadcast();
}

void UHyperTwistHigherDimensionalHUDWidget::HandleScramble()
{
	OnScrambleRequested.Broadcast();
}

void UHyperTwistHigherDimensionalHUDWidget::HandleReset()
{
	OnResetRequested.Broadcast();
}

void UHyperTwistHigherDimensionalHUDWidget::HandleSave()
{
	OnSaveRequested.Broadcast();
}

void UHyperTwistHigherDimensionalHUDWidget::HandleLoad()
{
	OnLoadRequested.Broadcast();
}

void UHyperTwistHigherDimensionalHUDWidget::HandleCoach()
{
	OnCoachRequested.Broadcast();
}

void UHyperTwistHigherDimensionalHUDWidget::HandleMenu()
{
	OnMenuRequested.Broadcast();
}
