#include "HyperTwistSimulation/HyperTwistClassicCubeHUDWidget.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/ScrollBox.h"
#include "Components/SizeBox.h"
#include "Components/TextBlock.h"
#include "Components/UniformGridPanel.h"
#include "Components/UniformGridSlot.h"
#include "Components/VerticalBox.h"
#include "Components/VerticalBoxSlot.h"
#include "GameFramework/GameModeBase.h"
#include "HyperTwistSimulation/HyperTwistClassicCubeOperatorSurface.h"

namespace HyperTwistClassicCubeHUDWidgetInternal
{
	const FLinearColor PanelColor(0.012f, 0.025f, 0.045f, 0.94f);
	const FLinearColor AccentColor(0.15f, 0.90f, 0.95f, 1.0f);
	const FLinearColor PrimaryTextColor(0.92f, 0.97f, 1.0f, 1.0f);
	const FLinearColor MutedTextColor(0.58f, 0.70f, 0.78f, 1.0f);
	const FLinearColor ButtonColor(0.04f, 0.16f, 0.23f, 1.0f);

	UObject* ResolveOperatorSurfaceObject(const UUserWidget* Widget)
	{
		if (Widget == nullptr || Widget->GetWorld() == nullptr)
		{
			return nullptr;
		}

		AGameModeBase* GameMode = Widget->GetWorld()->GetAuthGameMode();
		if (GameMode == nullptr
			|| !GameMode->GetClass()->ImplementsInterface(UHyperTwistClassicCubeOperatorSurface::StaticClass()))
		{
			return nullptr;
		}

		return GameMode;
	}
}

void UHyperTwistClassicCubeHUDWidget::NativeOnInitialized()
{
	Super::NativeOnInitialized();
	EnsureWidgetTreeBuilt();
}

void UHyperTwistClassicCubeHUDWidget::NativeConstruct()
{
	Super::NativeConstruct();
	EnsureWidgetTreeBuilt();
}

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

bool UHyperTwistClassicCubeHUDWidget::PrepareClassicCubeHudSurface()
{
	EnsureWidgetTreeBuilt();
	return IsClassicCubeHudSurfaceReady();
}

bool UHyperTwistClassicCubeHUDWidget::IsClassicCubeHudSurfaceReady() const
{
	return WidgetTree != nullptr
		&& WidgetTree->RootWidget != nullptr
		&& StatusTextBlock != nullptr
		&& TimerTextBlock != nullptr
		&& NewScrambleButton != nullptr
		&& HintButton != nullptr;
}

void UHyperTwistClassicCubeHUDWidget::SetHudLines(
	const FString& InStatusLine,
	const FString& InScrambleLine,
	const FString& InTimerLine,
	const FString& InInspectionLine,
	const FString& InResultLine,
	const FString& InControlsLine,
	const FString& InHintLine,
	const FString& InSolutionLine,
	const FString& InReplayLine,
	const FString& InLeaderboardLine,
	const FString& InModeLine,
	const FString& InTrainingLine
)
{
	EnsureWidgetTreeBuilt();

	if (StatusTextBlock != nullptr)
	{
		StatusTextBlock->SetText(FText::FromString(InStatusLine));
	}
	if (ScrambleTextBlock != nullptr)
	{
		ScrambleTextBlock->SetText(FText::FromString(InScrambleLine));
	}
	if (TimerTextBlock != nullptr)
	{
		TimerTextBlock->SetText(FText::FromString(InTimerLine));
	}
	if (InspectionTextBlock != nullptr)
	{
		InspectionTextBlock->SetText(FText::FromString(InInspectionLine));
	}
	if (ResultTextBlock != nullptr)
	{
		ResultTextBlock->SetText(FText::FromString(InResultLine));
	}
	if (ControlsTextBlock != nullptr)
	{
		ControlsTextBlock->SetText(FText::FromString(InControlsLine));
	}
	if (HintTextBlock != nullptr)
	{
		HintTextBlock->SetText(FText::FromString(InHintLine));
	}
	if (SolutionTextBlock != nullptr)
	{
		SolutionTextBlock->SetText(FText::FromString(InSolutionLine));
	}
	if (ReplayTextBlock != nullptr)
	{
		ReplayTextBlock->SetText(FText::FromString(InReplayLine));
	}
	if (LeaderboardTextBlock != nullptr)
	{
		LeaderboardTextBlock->SetText(FText::FromString(InLeaderboardLine));
	}
	if (ModeTextBlock != nullptr)
	{
		ModeTextBlock->SetText(FText::FromString(InModeLine));
	}
	if (TrainingTextBlock != nullptr)
	{
		TrainingTextBlock->SetText(FText::FromString(InTrainingLine));
		TrainingTextBlock->SetVisibility(
			InTrainingLine.IsEmpty()
				? ESlateVisibility::Collapsed
				: ESlateVisibility::HitTestInvisible);
	}
}

bool UHyperTwistClassicCubeHUDWidget::IsNewScrambleButtonHovered() const
{
	return bNewScrambleButtonHovered || (NewScrambleButton != nullptr && NewScrambleButton->IsHovered());
}

bool UHyperTwistClassicCubeHUDWidget::IsAnyActionButtonHovered() const
{
	auto IsHovered = [](const UButton* Button)
	{
		return Button != nullptr && Button->IsHovered();
	};

	return IsHovered(NewScrambleButton)
		|| IsHovered(HintButton)
		|| IsHovered(SubmitSolveButton)
		|| IsHovered(ModeToggleButton);
}

void UHyperTwistClassicCubeHUDWidget::SetModeToggleButtonLabel(const FString& InLabel)
{
	EnsureWidgetTreeBuilt();
	if (ModeToggleButtonLabel != nullptr)
	{
		ModeToggleButtonLabel->SetText(FText::FromString(InLabel));
	}
}

void UHyperTwistClassicCubeHUDWidget::EnsureWidgetTreeBuilt()
{
	if (WidgetTree == nullptr || WidgetTree->RootWidget != nullptr)
	{
		return;
	}

	UHorizontalBox* ViewportLayout = WidgetTree->ConstructWidget<UHorizontalBox>(
		UHorizontalBox::StaticClass(),
		TEXT("ClassicCubeHudRoot")
	);
	ViewportLayout->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
	WidgetTree->RootWidget = ViewportLayout;

	USizeBox* PanelSize = WidgetTree->ConstructWidget<USizeBox>(
		USizeBox::StaticClass(),
		TEXT("ClassicCubeHudPanelSize")
	);
	PanelSize->SetWidthOverride(520.0f);
	PanelSize->SetMaxDesiredHeight(1000.0f);
	if (UHorizontalBoxSlot* PanelSlot = ViewportLayout->AddChildToHorizontalBox(PanelSize))
	{
		PanelSlot->SetPadding(FMargin(18.0f));
		PanelSlot->SetVerticalAlignment(VAlign_Top);
	}

	UBorder* PanelBorder = WidgetTree->ConstructWidget<UBorder>(
		UBorder::StaticClass(),
		TEXT("ClassicCubeHudPanel")
	);
	PanelBorder->SetPadding(FMargin(18.0f));
	PanelBorder->SetBrushColor(HyperTwistClassicCubeHUDWidgetInternal::PanelColor);
	PanelSize->AddChild(PanelBorder);

	UScrollBox* PanelScroll = WidgetTree->ConstructWidget<UScrollBox>(
		UScrollBox::StaticClass(),
		TEXT("ClassicCubeHudScroll")
	);
	PanelBorder->AddChild(PanelScroll);

	UVerticalBox* RootLayout = WidgetTree->ConstructWidget<UVerticalBox>(
		UVerticalBox::StaticClass(),
		TEXT("ClassicCubeHudContent")
	);
	PanelScroll->AddChild(RootLayout);

	UTextBlock* TitleText = AddLine(
		RootLayout,
		TEXT("ClassicCubeHudTitle"),
		20,
		HyperTwistClassicCubeHUDWidgetInternal::AccentColor
	);
	if (TitleText != nullptr)
	{
		TitleText->SetText(FText::FromString(TEXT("CLASSIC CUBE // TRAINING")));
	}

	StatusTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudStatus"), 15, HyperTwistClassicCubeHUDWidgetInternal::PrimaryTextColor);
	TimerTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudTimer"), 27, HyperTwistClassicCubeHUDWidgetInternal::PrimaryTextColor);
	InspectionTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudInspection"), 14, HyperTwistClassicCubeHUDWidgetInternal::AccentColor);
	ScrambleTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudScramble"), 13, HyperTwistClassicCubeHUDWidgetInternal::MutedTextColor);
	ResultTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudResult"), 14, HyperTwistClassicCubeHUDWidgetInternal::PrimaryTextColor);
	TrainingTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudTraining"), 13, HyperTwistClassicCubeHUDWidgetInternal::AccentColor);
	HintTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudHint"), 13, HyperTwistClassicCubeHUDWidgetInternal::MutedTextColor);
	SolutionTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudSolution"), 13, HyperTwistClassicCubeHUDWidgetInternal::MutedTextColor);
	ModeTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudMode"), 13, HyperTwistClassicCubeHUDWidgetInternal::PrimaryTextColor);
	ReplayTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudReplay"), 12, HyperTwistClassicCubeHUDWidgetInternal::MutedTextColor);
	LeaderboardTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudLeaderboard"), 12, HyperTwistClassicCubeHUDWidgetInternal::MutedTextColor);
	ControlsTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudControls"), 12, HyperTwistClassicCubeHUDWidgetInternal::MutedTextColor);

	UUniformGridPanel* ActionGrid = WidgetTree->ConstructWidget<UUniformGridPanel>(
		UUniformGridPanel::StaticClass(),
		TEXT("ClassicCubeHudActionGrid")
	);
	ActionGrid->SetMinDesiredSlotWidth(220.0f);
	if (UVerticalBoxSlot* GridSlot = RootLayout->AddChildToVerticalBox(ActionGrid))
	{
		GridSlot->SetPadding(FMargin(0.0f, 12.0f, 0.0f, 0.0f));
	}

	NewScrambleButton = CreateActionButton(
		ActionGrid,
		TEXT("ClassicCubeHudNewScrambleButton"),
		TEXT("ClassicCubeHudNewScrambleButtonLabel"),
		NewScrambleButtonLabel,
		TEXT("New Scramble"),
		0,
		0
	);
	HintButton = CreateActionButton(
		ActionGrid,
		TEXT("ClassicCubeHudHintButton"),
		TEXT("ClassicCubeHudHintButtonLabel"),
		HintButtonLabel,
		TEXT("Hint"),
		0,
		1
	);
	SubmitSolveButton = CreateActionButton(
		ActionGrid,
		TEXT("ClassicCubeHudSubmitSolveButton"),
		TEXT("ClassicCubeHudSubmitSolveButtonLabel"),
		SubmitSolveButtonLabel,
		TEXT("Submit Solve"),
		1,
		0
	);
	ModeToggleButton = CreateActionButton(
		ActionGrid,
		TEXT("ClassicCubeHudModeToggleButton"),
		TEXT("ClassicCubeHudModeToggleButtonLabel"),
		ModeToggleButtonLabel,
		TEXT("Follow Along"),
		1,
		1
	);
	if (NewScrambleButton != nullptr)
	{
		NewScrambleButton->OnClicked.AddDynamic(this, &UHyperTwistClassicCubeHUDWidget::HandleNewScrambleClicked);
	}
	if (HintButton != nullptr)
	{
		HintButton->OnClicked.AddDynamic(this, &UHyperTwistClassicCubeHUDWidget::HandleHintClicked);
	}
	if (SubmitSolveButton != nullptr)
	{
		SubmitSolveButton->OnClicked.AddDynamic(this, &UHyperTwistClassicCubeHUDWidget::HandleSubmitSolveClicked);
	}
	if (ModeToggleButton != nullptr)
	{
		ModeToggleButton->OnClicked.AddDynamic(this, &UHyperTwistClassicCubeHUDWidget::HandleModeToggleClicked);
	}
	SetHudLines(
		TEXT("status: preparing classic cube lane"),
		TEXT("scramble: pending"),
		TEXT("timer: idle"),
		TEXT("inspection: pending"),
		TEXT("result: waiting for first attempt"),
		TEXT("controls: LMB clockwise, RMB counter-clockwise, touch clockwise, MMB drag orbit, wheel zoom, R scramble, H hint, Enter submit, F mode, F3 coach, Esc settings"),
		TEXT("hint: request solver guidance"),
		TEXT("solution: unavailable"),
		TEXT("replay: armed for local capture"),
		TEXT("leaderboard: no local best yet"),
		TEXT("mode: free play"),
		FString()
	);
}

UTextBlock* UHyperTwistClassicCubeHUDWidget::AddLine(
	UVerticalBox* Parent,
	const TCHAR* WidgetName,
	const int32 FontSize,
	const FLinearColor& Color
)
{
	if (WidgetTree == nullptr || Parent == nullptr)
	{
		return nullptr;
	}

	UTextBlock* TextBlock =
		WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), WidgetName);
	if (TextBlock == nullptr)
	{
		return nullptr;
	}
	TextBlock->SetAutoWrapText(true);
	TextBlock->SetColorAndOpacity(FSlateColor(Color));
	FSlateFontInfo Font = TextBlock->GetFont();
	Font.Size = FontSize;
	TextBlock->SetFont(Font);

	if (UVerticalBoxSlot* VerticalBoxSlot = Parent->AddChildToVerticalBox(TextBlock))
	{
		VerticalBoxSlot->SetPadding(FMargin(0.0f, 4.0f, 0.0f, 4.0f));
	}

	return TextBlock;
}

UButton* UHyperTwistClassicCubeHUDWidget::CreateActionButton(
	UUniformGridPanel* Parent,
	const TCHAR* ButtonName,
	const TCHAR* LabelName,
	TObjectPtr<UTextBlock>& OutLabel,
	const FString& InitialLabel,
	const int32 Row,
	const int32 Column
)
{
	if (WidgetTree == nullptr || Parent == nullptr)
	{
		return nullptr;
	}

	UButton* ActionButton = WidgetTree->ConstructWidget<UButton>(
		UButton::StaticClass(),
		ButtonName
	);
	if (ActionButton == nullptr)
	{
		return nullptr;
	}
	ActionButton->SetBackgroundColor(HyperTwistClassicCubeHUDWidgetInternal::ButtonColor);

	OutLabel = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), LabelName);
	if (OutLabel != nullptr)
	{
		OutLabel->SetText(FText::FromString(InitialLabel));
		OutLabel->SetAutoWrapText(true);
		OutLabel->SetJustification(ETextJustify::Center);
		OutLabel->SetColorAndOpacity(FSlateColor(HyperTwistClassicCubeHUDWidgetInternal::PrimaryTextColor));
		FSlateFontInfo Font = OutLabel->GetFont();
		Font.Size = 13;
		OutLabel->SetFont(Font);
		ActionButton->AddChild(OutLabel);
	}

	Parent->AddChildToUniformGrid(ActionButton, Row, Column);

	if (UButtonSlot* ButtonSlot = Cast<UButtonSlot>(OutLabel != nullptr ? OutLabel->Slot : nullptr))
	{
		ButtonSlot->SetPadding(FMargin(12.0f, 6.0f, 12.0f, 6.0f));
		ButtonSlot->SetHorizontalAlignment(HAlign_Center);
		ButtonSlot->SetVerticalAlignment(VAlign_Center);
	}

	return ActionButton;
}

void UHyperTwistClassicCubeHUDWidget::HandleNewScrambleClicked()
{
	bNewScrambleButtonHovered = false;
	if (UObject* OperatorSurface =
			HyperTwistClassicCubeHUDWidgetInternal::ResolveOperatorSurfaceObject(this))
	{
		IHyperTwistClassicCubeOperatorSurface::Execute_RequestClassicCubeFreshAttempt(
			OperatorSurface
		);
	}
}

void UHyperTwistClassicCubeHUDWidget::HandleHintClicked()
{
	if (UObject* OperatorSurface =
			HyperTwistClassicCubeHUDWidgetInternal::ResolveOperatorSurfaceObject(this))
	{
		IHyperTwistClassicCubeOperatorSurface::Execute_RequestClassicCubeHint(OperatorSurface);
	}
}

void UHyperTwistClassicCubeHUDWidget::HandleSubmitSolveClicked()
{
	if (UObject* OperatorSurface =
			HyperTwistClassicCubeHUDWidgetInternal::ResolveOperatorSurfaceObject(this))
	{
		IHyperTwistClassicCubeOperatorSurface::Execute_RequestClassicCubeSubmitSolve(
			OperatorSurface
		);
	}
}

void UHyperTwistClassicCubeHUDWidget::HandleModeToggleClicked()
{
	if (UObject* OperatorSurface =
			HyperTwistClassicCubeHUDWidgetInternal::ResolveOperatorSurfaceObject(this))
	{
		IHyperTwistClassicCubeOperatorSurface::Execute_RequestClassicCubeToggleFollowAlongMode(
			OperatorSurface
		);
	}
}
