// Copyright Pacificao. All Rights Reserved.

#include "AgrarianDemoNoticeWidget.h"

#include "Engine/World.h"
#include "Rendering/DrawElements.h"
#include "Styling/CoreStyle.h"

namespace
{
struct FAgrarianCreditCard
{
	const TCHAR* Name;
	const TCHAR* Role;
	FLinearColor AccentColor;
	int32 IllustrationIndex;
	float HoldSeconds;
};

const FAgrarianCreditCard CreditCards[] = {
	{ TEXT("Nathan Slaven"), TEXT("Lead Developer"), FLinearColor(0.70f, 0.90f, 0.46f, 1.0f), 0, 2.05f },
	{ TEXT("Hunter Slaven"), TEXT("Junior Developer"), FLinearColor(0.36f, 0.72f, 0.96f, 1.0f), 1, 2.05f },
	{ TEXT("Lisa Reiley"), TEXT("Quality Control"), FLinearColor(0.95f, 0.72f, 0.42f, 1.0f), 2, 2.05f },
	{ TEXT("River Slaven"), TEXT("Alpha Game Tester"), FLinearColor(0.56f, 0.82f, 0.66f, 1.0f), 3, 2.05f },
	{ TEXT("Fisher Slaven"), TEXT("Beta Game Tester"), FLinearColor(0.56f, 0.62f, 0.98f, 1.0f), 4, 2.05f },
	{ TEXT("Funding"), TEXT("Cherished individuals, Pacificao seed funding, and Lina Family Investment Funds"), FLinearColor(0.92f, 0.84f, 0.54f, 1.0f), 5, 3.0f },
};

float SmoothStep(float Value)
{
	const float ClampedValue = FMath::Clamp(Value, 0.0f, 1.0f);
	return ClampedValue * ClampedValue * (3.0f - (2.0f * ClampedValue));
}
}

void UAgrarianDemoNoticeWidget::NativeConstruct()
{
	Super::NativeConstruct();
	CreditsStartTimeSeconds = GetWorld() ? GetWorld()->GetTimeSeconds() : 0.0f;
}

int32 UAgrarianDemoNoticeWidget::NativePaint(
	const FPaintArgs& Args,
	const FGeometry& AllottedGeometry,
	const FSlateRect& MyCullingRect,
	FSlateWindowElementList& OutDrawElements,
	int32 LayerId,
	const FWidgetStyle& InWidgetStyle,
	bool bParentEnabled) const
{
	LayerId = Super::NativePaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);

	const FVector2D Size = AllottedGeometry.GetLocalSize();
	const float PanelWidth = FMath::Min(860.0f, FMath::Max(320.0f, Size.X - 96.0f));
	const float PanelHeight = 210.0f;
	const FVector2D PanelPosition((Size.X - PanelWidth) * 0.5f, 42.0f);

	FSlateDrawElement::MakeBox(
		OutDrawElements,
		++LayerId,
		AllottedGeometry.ToPaintGeometry(FVector2f(PanelWidth, PanelHeight), FSlateLayoutTransform(FVector2f(PanelPosition))),
		FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")),
		ESlateDrawEffect::None,
		FLinearColor(0.02f, 0.03f, 0.025f, 0.82f));

	FSlateDrawElement::MakeBox(
		OutDrawElements,
		++LayerId,
		AllottedGeometry.ToPaintGeometry(FVector2f(PanelWidth, 3.0f), FSlateLayoutTransform(FVector2f(PanelPosition))),
		FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")),
		ESlateDrawEffect::None,
		FLinearColor(0.45f, 0.72f, 0.40f, 1.0f));

	const FSlateFontInfo MottoFont = FCoreStyle::GetDefaultFontStyle("Bold", 30);
	const FSlateFontInfo VersionFont = FCoreStyle::GetDefaultFontStyle("Regular", 18);
	const FSlateFontInfo NoticeFont = FCoreStyle::GetDefaultFontStyle("Regular", 16);

	DrawCenteredText(OutDrawElements, LayerId, AllottedGeometry, Motto, PanelPosition.Y + 34.0f, MottoFont, FLinearColor(0.86f, 0.94f, 0.78f, 1.0f));
	DrawCenteredText(OutDrawElements, LayerId, AllottedGeometry, VersionLabel, PanelPosition.Y + 94.0f, VersionFont, FLinearColor(0.82f, 0.86f, 0.78f, 1.0f));
	DrawCenteredText(OutDrawElements, LayerId, AllottedGeometry, DemoNotice, PanelPosition.Y + 126.0f, NoticeFont, FLinearColor(0.78f, 0.82f, 0.75f, 1.0f));
	DrawCenteredText(OutDrawElements, LayerId, AllottedGeometry, CopyrightNotice, PanelPosition.Y + 158.0f, NoticeFont, FLinearColor(0.66f, 0.70f, 0.64f, 1.0f));

	DrawCinematicCredits(OutDrawElements, LayerId, AllottedGeometry);

	return LayerId;
}

void UAgrarianDemoNoticeWidget::DrawCenteredText(
	FSlateWindowElementList& OutDrawElements,
	int32& LayerId,
	const FGeometry& AllottedGeometry,
	const FText& Text,
	float Y,
	const FSlateFontInfo& Font,
	const FLinearColor& Color) const
{
	const FVector2D Size = AllottedGeometry.GetLocalSize();
	const FString TextString = Text.ToString();
	const float EstimatedWidth = FMath::Min(Size.X - 96.0f, static_cast<float>(TextString.Len()) * Font.Size * 0.52f);
	const FVector2D TextPosition((Size.X - EstimatedWidth) * 0.5f, Y);

	FSlateDrawElement::MakeText(
		OutDrawElements,
		++LayerId,
		AllottedGeometry.ToPaintGeometry(FVector2f(EstimatedWidth, Font.Size + 12.0f), FSlateLayoutTransform(FVector2f(TextPosition))),
		Text,
		Font,
		ESlateDrawEffect::None,
		Color);
}

void UAgrarianDemoNoticeWidget::DrawTextAt(
	FSlateWindowElementList& OutDrawElements,
	int32& LayerId,
	const FGeometry& AllottedGeometry,
	const FText& Text,
	const FVector2D& Position,
	float Width,
	const FSlateFontInfo& Font,
	const FLinearColor& Color) const
{
	FSlateDrawElement::MakeText(
		OutDrawElements,
		++LayerId,
		AllottedGeometry.ToPaintGeometry(FVector2f(FMath::Max(96.0f, Width), Font.Size + 18.0f), FSlateLayoutTransform(FVector2f(Position))),
		Text,
		Font,
		ESlateDrawEffect::None,
		Color);
}

void UAgrarianDemoNoticeWidget::DrawCinematicCredits(
	FSlateWindowElementList& OutDrawElements,
	int32& LayerId,
	const FGeometry& AllottedGeometry) const
{
	const UWorld* World = GetWorld();
	if (!World)
	{
		return;
	}

	const FVector2D Size = AllottedGeometry.GetLocalSize();
	const float Elapsed = World->GetTimeSeconds() - CreditsStartTimeSeconds;
	const float IntroDelay = 0.55f;
	const float SlamSeconds = 0.28f;
	const float ExitSeconds = 0.48f;
	const float GapSeconds = 0.16f;

	float TimelineCursor = IntroDelay;
	int32 ActiveIndex = INDEX_NONE;
	float LocalTime = 0.0f;
	for (int32 Index = 0; Index < UE_ARRAY_COUNT(CreditCards); ++Index)
	{
		const float CardDuration = SlamSeconds + CreditCards[Index].HoldSeconds + ExitSeconds + GapSeconds;
		if (Elapsed >= TimelineCursor && Elapsed < TimelineCursor + CardDuration)
		{
			ActiveIndex = Index;
			LocalTime = Elapsed - TimelineCursor;
			break;
		}
		TimelineCursor += CardDuration;
	}

	if (ActiveIndex == INDEX_NONE)
	{
		return;
	}

	const FAgrarianCreditCard& Card = CreditCards[ActiveIndex];
	const bool bEntering = LocalTime < SlamSeconds;
	const bool bExiting = LocalTime > SlamSeconds + Card.HoldSeconds;
	const float EnterAlpha = bEntering ? SmoothStep(LocalTime / SlamSeconds) : 1.0f;
	const float ExitAlpha = bExiting ? 1.0f - SmoothStep((LocalTime - SlamSeconds - Card.HoldSeconds) / ExitSeconds) : 1.0f;
	const float Alpha = FMath::Clamp(EnterAlpha * ExitAlpha, 0.0f, 1.0f);
	const float ImpactOvershoot = bEntering ? FMath::Sin((LocalTime / SlamSeconds) * PI) * 28.0f : 0.0f;

	const float PanelWidth = FMath::Min(1120.0f, FMath::Max(420.0f, Size.X - 112.0f));
	const float PanelHeight = FMath::Min(330.0f, FMath::Max(220.0f, Size.Y - 340.0f));
	const FVector2D TargetPosition((Size.X - PanelWidth) * 0.5f, FMath::Max(275.0f, (Size.Y - PanelHeight) * 0.5f + 72.0f));
	const float EnterOffset = bEntering ? FMath::Lerp(-Size.X * 0.85f, ImpactOvershoot, SmoothStep(LocalTime / SlamSeconds)) : 0.0f;
	const float ExitOffset = bExiting ? FMath::Lerp(0.0f, Size.X * 0.9f, SmoothStep((LocalTime - SlamSeconds - Card.HoldSeconds) / ExitSeconds)) : 0.0f;
	const FVector2D PanelPosition(TargetPosition.X + EnterOffset + ExitOffset, TargetPosition.Y);

	FSlateDrawElement::MakeBox(
		OutDrawElements,
		++LayerId,
		AllottedGeometry.ToPaintGeometry(FVector2f(Size), FSlateLayoutTransform(FVector2f::ZeroVector)),
		FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")),
		ESlateDrawEffect::None,
		FLinearColor(0.0f, 0.0f, 0.0f, 0.34f * Alpha));

	FSlateDrawElement::MakeBox(
		OutDrawElements,
		++LayerId,
		AllottedGeometry.ToPaintGeometry(FVector2f(PanelWidth, PanelHeight), FSlateLayoutTransform(FVector2f(PanelPosition))),
		FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")),
		ESlateDrawEffect::None,
		FLinearColor(0.015f, 0.018f, 0.014f, 0.92f * Alpha));

	FSlateDrawElement::MakeBox(
		OutDrawElements,
		++LayerId,
		AllottedGeometry.ToPaintGeometry(FVector2f(PanelWidth, 5.0f), FSlateLayoutTransform(FVector2f(PanelPosition))),
		FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")),
		ESlateDrawEffect::None,
		FLinearColor(Card.AccentColor.R, Card.AccentColor.G, Card.AccentColor.B, Alpha));

	const FVector2D ArtSize(FMath::Min(275.0f, PanelWidth * 0.32f), PanelHeight - 64.0f);
	const FVector2D ArtPosition(PanelPosition.X + 32.0f, PanelPosition.Y + 32.0f);
	DrawCreditIllustration(OutDrawElements, LayerId, AllottedGeometry, ArtPosition, ArtSize, Card.IllustrationIndex, Card.AccentColor, Alpha);

	const float TextX = ArtPosition.X + ArtSize.X + 42.0f;
	const float TextWidth = PanelPosition.X + PanelWidth - TextX - 36.0f;
	const FSlateFontInfo NameFont = FCoreStyle::GetDefaultFontStyle("Bold", ActiveIndex == 5 ? 46 : 56);
	const FSlateFontInfo RoleFont = FCoreStyle::GetDefaultFontStyle("Regular", ActiveIndex == 5 ? 22 : 28);
	const FSlateFontInfo LabelFont = FCoreStyle::GetDefaultFontStyle("Regular", 16);
	const float NameY = PanelPosition.Y + (ActiveIndex == 5 ? 76.0f : 92.0f);

	DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(Card.Name), FVector2D(TextX, NameY), TextWidth, NameFont, FLinearColor(0.94f, 0.98f, 0.88f, Alpha));
	DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(Card.Role), FVector2D(TextX + 4.0f, NameY + 76.0f), TextWidth, RoleFont, FLinearColor(Card.AccentColor.R, Card.AccentColor.G, Card.AccentColor.B, Alpha));
	DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("Agrarian startup credits")), FVector2D(TextX + 6.0f, PanelPosition.Y + PanelHeight - 58.0f), TextWidth, LabelFont, FLinearColor(0.64f, 0.70f, 0.60f, 0.82f * Alpha));
}

void UAgrarianDemoNoticeWidget::DrawCreditIllustration(
	FSlateWindowElementList& OutDrawElements,
	int32& LayerId,
	const FGeometry& AllottedGeometry,
	const FVector2D& Position,
	const FVector2D& Size,
	int32 IllustrationIndex,
	const FLinearColor& AccentColor,
	float Alpha) const
{
	FSlateDrawElement::MakeBox(
		OutDrawElements,
		++LayerId,
		AllottedGeometry.ToPaintGeometry(FVector2f(Size), FSlateLayoutTransform(FVector2f(Position))),
		FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")),
		ESlateDrawEffect::None,
		FLinearColor(0.07f, 0.08f, 0.065f, 0.96f * Alpha));

	const FVector2D Center = Position + (Size * 0.5f);
	const FLinearColor SoftAccent(AccentColor.R, AccentColor.G, AccentColor.B, 0.28f * Alpha);
	const FLinearColor StrongAccent(AccentColor.R, AccentColor.G, AccentColor.B, Alpha);

	auto DrawBox = [&](const FVector2D& BoxPosition, const FVector2D& BoxSize, const FLinearColor& Color)
	{
		FSlateDrawElement::MakeBox(
			OutDrawElements,
			++LayerId,
			AllottedGeometry.ToPaintGeometry(FVector2f(BoxSize), FSlateLayoutTransform(FVector2f(BoxPosition))),
			FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")),
			ESlateDrawEffect::None,
			Color);
	};

	auto DrawLine = [&](const TArray<FVector2f>& Points, const FLinearColor& Color, float Thickness)
	{
		FSlateDrawElement::MakeLines(
			OutDrawElements,
			++LayerId,
			AllottedGeometry.ToPaintGeometry(),
			Points,
			ESlateDrawEffect::None,
			Color,
			true,
			Thickness);
	};

	DrawBox(Position + FVector2D(16.0f, 16.0f), FVector2D(Size.X - 32.0f, Size.Y - 32.0f), FLinearColor(0.0f, 0.0f, 0.0f, 0.22f * Alpha));

	if (IllustrationIndex == 0)
	{
		DrawBox(Center + FVector2D(-72.0f, -46.0f), FVector2D(144.0f, 82.0f), SoftAccent);
		DrawBox(Center + FVector2D(-62.0f, -36.0f), FVector2D(124.0f, 62.0f), FLinearColor(0.02f, 0.03f, 0.025f, Alpha));
		DrawLine({ FVector2f(Center.X - 42.0f, Center.Y - 8.0f), FVector2f(Center.X - 8.0f, Center.Y - 28.0f), FVector2f(Center.X + 42.0f, Center.Y + 18.0f) }, StrongAccent, 4.0f);
		DrawBox(Center + FVector2D(-34.0f, 48.0f), FVector2D(68.0f, 10.0f), StrongAccent);
	}
	else if (IllustrationIndex == 1)
	{
		DrawLine({ FVector2f(Center.X, Center.Y + 58.0f), FVector2f(Center.X, Center.Y - 48.0f) }, StrongAccent, 5.0f);
		DrawLine({ FVector2f(Center.X, Center.Y - 8.0f), FVector2f(Center.X - 48.0f, Center.Y - 42.0f), FVector2f(Center.X - 74.0f, Center.Y - 20.0f) }, StrongAccent, 5.0f);
		DrawLine({ FVector2f(Center.X, Center.Y + 2.0f), FVector2f(Center.X + 52.0f, Center.Y - 34.0f), FVector2f(Center.X + 80.0f, Center.Y - 10.0f) }, StrongAccent, 5.0f);
		DrawBox(Center + FVector2D(-84.0f, 64.0f), FVector2D(168.0f, 8.0f), SoftAccent);
	}
	else if (IllustrationIndex == 2)
	{
		DrawBox(Center + FVector2D(-58.0f, -74.0f), FVector2D(116.0f, 148.0f), FLinearColor(0.93f, 0.88f, 0.72f, 0.22f * Alpha));
		DrawBox(Center + FVector2D(-34.0f, -88.0f), FVector2D(68.0f, 20.0f), StrongAccent);
		DrawLine({ FVector2f(Center.X - 32.0f, Center.Y - 26.0f), FVector2f(Center.X - 12.0f, Center.Y - 6.0f), FVector2f(Center.X + 42.0f, Center.Y - 48.0f) }, StrongAccent, 5.0f);
		DrawBox(Center + FVector2D(-34.0f, 28.0f), FVector2D(68.0f, 5.0f), StrongAccent);
	}
	else if (IllustrationIndex == 3)
	{
		DrawBox(Center + FVector2D(-72.0f, -28.0f), FVector2D(144.0f, 56.0f), SoftAccent);
		DrawLine({ FVector2f(Center.X - 70.0f, Center.Y), FVector2f(Center.X - 18.0f, Center.Y - 58.0f), FVector2f(Center.X + 32.0f, Center.Y + 44.0f), FVector2f(Center.X + 78.0f, Center.Y - 18.0f) }, StrongAccent, 5.0f);
		DrawBox(Center + FVector2D(-16.0f, -16.0f), FVector2D(32.0f, 32.0f), FLinearColor(0.92f, 0.96f, 0.86f, 0.80f * Alpha));
	}
	else if (IllustrationIndex == 4)
	{
		DrawBox(Center + FVector2D(-64.0f, -58.0f), FVector2D(128.0f, 116.0f), SoftAccent);
		DrawLine({ FVector2f(Center.X - 44.0f, Center.Y - 8.0f), FVector2f(Center.X - 8.0f, Center.Y + 28.0f), FVector2f(Center.X + 54.0f, Center.Y - 36.0f) }, StrongAccent, 5.0f);
		DrawBox(Center + FVector2D(-74.0f, 74.0f), FVector2D(148.0f, 8.0f), StrongAccent);
		DrawBox(Center + FVector2D(-74.0f, -82.0f), FVector2D(148.0f, 8.0f), StrongAccent);
	}
	else
	{
		DrawLine({ FVector2f(Center.X, Center.Y + 72.0f), FVector2f(Center.X, Center.Y - 36.0f) }, StrongAccent, 5.0f);
		DrawLine({ FVector2f(Center.X, Center.Y - 12.0f), FVector2f(Center.X - 52.0f, Center.Y - 50.0f), FVector2f(Center.X - 80.0f, Center.Y - 28.0f) }, StrongAccent, 5.0f);
		DrawLine({ FVector2f(Center.X, Center.Y - 6.0f), FVector2f(Center.X + 52.0f, Center.Y - 54.0f), FVector2f(Center.X + 84.0f, Center.Y - 28.0f) }, StrongAccent, 5.0f);
		DrawBox(Center + FVector2D(-92.0f, 78.0f), FVector2D(184.0f, 8.0f), SoftAccent);
		DrawBox(Center + FVector2D(-48.0f, 56.0f), FVector2D(96.0f, 8.0f), StrongAccent);
	}
}
