#include "UIButton_Base.h"

#include "CommonTextBlock.h"
#include "Components/Image.h"
#include "Components/OverlaySlot.h"
#include "Engine/DataTable.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "Materials/MaterialInterface.h"
#include "Styling/SlateBrush.h"
#include "Styling/SlateTypes.h"
#include "UObject/ConstructorHelpers.h"
#include "UI/UIStyleRow.h"
#include "UI/UIStyleTokens.h"

namespace
{
	constexpr float MinTransition = 0.001f;

	// Params do material UI_M_Button (Bg).
	static const FName P_TopColour("TopColour");
	static const FName P_BottomColour("BottomColour");
	static const FName P_GlowColour("GlowColour");
	static const FName P_GlowIntensity("GlowIntensity");
	static const FName P_CornerColour("CornerColour");
	static const FName P_CornerLength("CornerLength");
	static const FName P_CornerThickness("CornerThickness");
	static const FName P_FrameColour("FrameColour");
	static const FName P_AspectRatio("AspectRatio");
	static const FName P_GlowEdge("GlowEdge");
	static const FName P_GlowSoftness("GlowSoftness");

	FORCEINLINE float EaseOutCubic(float Alpha)
	{
		return 1.f - FMath::Pow(1.f - Alpha, 3.f);
	}
}

UUIButton_Base::UUIButton_Base(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	// Materiais default (fallback quando o brush da imagem não traz material).
	static ConstructorHelpers::FObjectFinder<UMaterialInterface> BgMatFinder(
		TEXT("/Game/ZMMO/UI/Themes/Default/Button/UI_M_Button.UI_M_Button"));
	if (BgMatFinder.Succeeded()) { BgMaterial = BgMatFinder.Object; }

	static ConstructorHelpers::FObjectFinder<UMaterialInterface> GlowMatFinder(
		TEXT("/Game/ZMMO/UI/Themes/Default/Button/UI_M_Button_Glow.UI_M_Button_Glow"));
	if (GlowMatFinder.Succeeded()) { GlowMaterial = GlowMatFinder.Object; }

	static ConstructorHelpers::FObjectFinder<UMaterialInterface> CornerMatFinder(
		TEXT("/Game/ZMMO/UI/Themes/Default/Button/UI_M_CornerBrackets.UI_M_CornerBrackets"));
	if (CornerMatFinder.Succeeded()) { CornerMaterial = CornerMatFinder.Object; }

	static ConstructorHelpers::FObjectFinder<UMaterialInterface> FrameMatFinder(
		TEXT("/Game/ZMMO/UI/Themes/Default/Button/UI_M_Button_Frame.UI_M_Button_Frame"));
	if (FrameMatFinder.Succeeded()) { FrameMaterial = FrameMatFinder.Object; }

	// Defaults = btn-primary (buttons_showcase.html). Cores em valores sRGB
	// diretos (mesma convenção do UUIProgressBar_Base — UI sem gamma).

	// Default (repouso)
	DefaultVisual.TopColour     = FLinearColor(0.910f, 0.753f, 0.376f, 0.18f);
	DefaultVisual.BottomColour  = FLinearColor(0.659f, 0.471f, 0.188f, 0.10f);
	DefaultVisual.BorderColour  = FLinearColor(0.831f, 0.659f, 0.314f, 0.28f);
	DefaultVisual.BorderWidth   = 1.f;
	DefaultVisual.GlowColour    = FLinearColor(0.f, 0.f, 0.f, 0.5f);   // drop shadow (escura)
	DefaultVisual.GlowIntensity = 0.7f;
	DefaultVisual.TextColour    = FLinearColor(1.f, 0.847f, 0.533f, 1.f);
	DefaultVisual.Scale         = 1.f;
	DefaultVisual.RenderOpacity = 1.f;

	// Hover — gradient mais forte, borda gold, glow aceso, leve zoom.
	HoveredVisual.TopColour     = FLinearColor(0.910f, 0.753f, 0.376f, 0.32f);
	HoveredVisual.BottomColour  = FLinearColor(0.659f, 0.471f, 0.188f, 0.18f);
	HoveredVisual.BorderColour  = FLinearColor(0.910f, 0.753f, 0.439f, 1.f);
	HoveredVisual.BorderWidth   = 1.f;
	HoveredVisual.GlowColour    = FLinearColor(0.f, 0.f, 0.f, 0.55f);  // sombra mais forte (elevado)
	HoveredVisual.GlowIntensity = 0.9f;
	HoveredVisual.TextColour    = FLinearColor(1.f, 0.847f, 0.533f, 1.f);
	HoveredVisual.Scale         = 1.02f;
	HoveredVisual.RenderOpacity = 1.f;

	// Pressed/Active — gradient invertido, encolhe.
	PressedVisual.TopColour     = FLinearColor(0.659f, 0.471f, 0.188f, 0.25f);
	PressedVisual.BottomColour  = FLinearColor(0.910f, 0.753f, 0.376f, 0.15f);
	PressedVisual.BorderColour  = FLinearColor(0.910f, 0.753f, 0.439f, 1.f);
	PressedVisual.BorderWidth   = 1.f;
	PressedVisual.GlowColour    = FLinearColor(0.f, 0.f, 0.f, 0.5f);   // sombra menor (afundou)
	PressedVisual.GlowIntensity = 0.5f;
	PressedVisual.TextColour    = FLinearColor(1.f, 0.847f, 0.533f, 1.f);
	PressedVisual.Scale         = 0.98f;
	PressedVisual.RenderOpacity = 1.f;

	// Disabled — quase apagado, texto escuro, opacity 0.6.
	DisabledVisual.TopColour     = FLinearColor(1.f, 1.f, 1.f, 0.02f);
	DisabledVisual.BottomColour  = FLinearColor(1.f, 1.f, 1.f, 0.02f);
	DisabledVisual.BorderColour  = FLinearColor(1.f, 1.f, 1.f, 0.06f);
	DisabledVisual.BorderWidth   = 1.f;
	DisabledVisual.GlowColour    = FLinearColor(0.f, 0.f, 0.f, 0.25f); // sombra fraca
	DisabledVisual.GlowIntensity = 0.3f;
	DisabledVisual.TextColour    = FLinearColor(0.227f, 0.220f, 0.188f, 1.f);
	DisabledVisual.Scale         = 1.f;
	DisabledVisual.RenderOpacity = 0.6f;

	// Selected (tabs/toggles) — gold suave persistente, sem zoom.
	SelectedVisual.TopColour     = FLinearColor(0.910f, 0.753f, 0.376f, 0.20f);
	SelectedVisual.BottomColour  = FLinearColor(0.659f, 0.471f, 0.188f, 0.12f);
	SelectedVisual.BorderColour  = FLinearColor(0.831f, 0.659f, 0.314f, 0.55f);
	SelectedVisual.BorderWidth   = 1.f;
	SelectedVisual.GlowColour    = FLinearColor(0.f, 0.f, 0.f, 0.5f);
	SelectedVisual.GlowIntensity = 0.75f;
	SelectedVisual.TextColour    = FLinearColor(1.f, 0.847f, 0.533f, 1.f);
	SelectedVisual.Scale         = 1.f;
	SelectedVisual.RenderOpacity = 1.f;

	CurrentVisual = DefaultVisual;
}

void UUIButton_Base::NativePreConstruct()
{
	Super::NativePreConstruct();

	EnsureMID();

	// Padding/centralização do texto via slot do Overlay (design-time + runtime).
	if (ButtonText)
	{
		if (UOverlaySlot* OS = Cast<UOverlaySlot>(ButtonText->Slot))
		{
			OS->SetPadding(ContentPadding);
			OS->SetHorizontalAlignment(HAlign_Center);
			OS->SetVerticalAlignment(VAlign_Center);
		}
	}

	// GlowFx (drop shadow): vaza pra fora via padding negativo + deslocado pra
	// baixo por GlowOffsetY (a sombra aparece mais embaixo, estilo box-shadow).
	if (GlowFx)
	{
		if (UOverlaySlot* OS = Cast<UOverlaySlot>(GlowFx->Slot))
		{
			OS->SetPadding(FMargin(-GlowExtent, -GlowExtent + GlowOffsetY,
			                       -GlowExtent, -GlowExtent - GlowOffsetY));
			OS->SetHorizontalAlignment(HAlign_Fill);
			OS->SetVerticalAlignment(VAlign_Fill);
		}
	}

	// Corners puxado pra dentro (CornerInset>0) ou pra fora/abraçando (<0).
	if (Corners)
	{
		if (UOverlaySlot* OS = Cast<UOverlaySlot>(Corners->Slot))
		{
			OS->SetPadding(FMargin(CornerInset));
			OS->SetHorizontalAlignment(HAlign_Fill);
			OS->SetVerticalAlignment(VAlign_Fill);
		}
	}

	CurrentVisual = DefaultVisual;
	bTransitioning = false;
	ApplyVisual(CurrentVisual);
}

void UUIButton_Base::NativeConstruct()
{
	Super::NativeConstruct();

	EnsureMID();
	bHoveredState = bPressedState = bSelectedState = false;
	bDisabledState = !GetIsEnabled();

	// Estado inicial sem animar.
	const FUIButtonStateVisual& Target =
		bDisabledState ? DisabledVisual
		: bSelectedState ? SelectedVisual
		: DefaultVisual;
	CurrentVisual = Target;
	bTransitioning = false;
	ApplyVisual(CurrentVisual);
}

void UUIButton_Base::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
	Super::NativeTick(MyGeometry, InDeltaTime);

	if (!bTransitioning)
	{
		return;
	}

	TransitionElapsed += InDeltaTime;
	const float Duration = FMath::Max(TransitionSeconds, MinTransition);
	const float Alpha = FMath::Clamp(TransitionElapsed / Duration, 0.f, 1.f);
	CurrentVisual = LerpVisual(FromVisual, ToVisual, EaseOutCubic(Alpha));
	ApplyVisual(CurrentVisual);

	if (Alpha >= 1.f)
	{
		CurrentVisual = ToVisual;
		ApplyVisual(CurrentVisual);
		bTransitioning = false;
	}
}

// === Hooks de estado ===

void UUIButton_Base::NativeOnHovered()
{
	Super::NativeOnHovered();
	bHoveredState = true;
	RefreshState();
}

void UUIButton_Base::NativeOnUnhovered()
{
	Super::NativeOnUnhovered();
	bHoveredState = false;
	RefreshState();
}

void UUIButton_Base::NativeOnPressed()
{
	Super::NativeOnPressed();
	bPressedState = true;
	RefreshState();
}

void UUIButton_Base::NativeOnReleased()
{
	Super::NativeOnReleased();
	bPressedState = false;
	RefreshState();
}

void UUIButton_Base::NativeOnEnabled()
{
	Super::NativeOnEnabled();
	bDisabledState = false;
	RefreshState();
}

void UUIButton_Base::NativeOnDisabled()
{
	Super::NativeOnDisabled();
	bDisabledState = true;
	RefreshState();
}

void UUIButton_Base::NativeOnSelected(bool bBroadcast)
{
	Super::NativeOnSelected(bBroadcast);
	bSelectedState = true;
	RefreshState();
}

void UUIButton_Base::NativeOnDeselected(bool bBroadcast)
{
	Super::NativeOnDeselected(bBroadcast);
	bSelectedState = false;
	RefreshState();
}

// === Internos ===

void UUIButton_Base::RefreshState()
{
	const FUIButtonStateVisual& Target =
		bDisabledState ? DisabledVisual
		: bPressedState ? PressedVisual
		: bHoveredState ? HoveredVisual
		: bSelectedState ? SelectedVisual
		: DefaultVisual;
	GoToState(Target);
}

void UUIButton_Base::GoToState(const FUIButtonStateVisual& Target)
{
	if (TransitionSeconds <= MinTransition)
	{
		CurrentVisual = Target;
		bTransitioning = false;
		ApplyVisual(CurrentVisual);
		return;
	}
	FromVisual = CurrentVisual;
	ToVisual = Target;
	TransitionElapsed = 0.f;
	bTransitioning = true;
}

void UUIButton_Base::ApplyVisual(const FUIButtonStateVisual& V)
{
	// Fundo: só gradient (o glow agora é a camada externa GlowFx).
	if (BgMID)
	{
		BgMID->SetVectorParameterValue(P_TopColour, V.TopColour);
		BgMID->SetVectorParameterValue(P_BottomColour, V.BottomColour);
	}
	// Halo externo (sai pra fora).
	if (GlowMID)
	{
		GlowMID->SetVectorParameterValue(P_GlowColour, V.GlowColour);
		GlowMID->SetScalarParameterValue(P_GlowIntensity, V.GlowIntensity);
	}
	// Cantos: cor acompanha a borda do estado (acende no hover); tamanho/peso
	// vêm das UPROPERTYs (estáticos por instância).
	if (CornerMID)
	{
		CornerMID->SetVectorParameterValue(P_CornerColour, V.BorderColour);
		CornerMID->SetScalarParameterValue(P_CornerLength, CornerLength);
		CornerMID->SetScalarParameterValue(P_CornerThickness, CornerThickness);
	}
	// Frame (material): mesma cor de borda do estado.
	if (BorderMID)
	{
		BorderMID->SetVectorParameterValue(P_FrameColour, V.BorderColour);
	}

	const bool bSimple  = (BorderStyle == EUIButtonBorderStyle::Simple);
	const bool bFrame   = (BorderStyle == EUIButtonBorderStyle::Frame);
	const bool bCorners = bShowCorners; // INDEPENDENTE da borda

	// Bg: canto arredondado sempre; a linha (outline) só no modo Simple.
	if (Bg)
	{
		FSlateBrush B = Bg->GetBrush();
		B.DrawAs = ESlateBrushDrawType::RoundedBox;
		FSlateBrushOutlineSettings Outline = B.OutlineSettings;
		Outline.CornerRadii = FVector4(CornerRadius, CornerRadius, CornerRadius, CornerRadius);
		Outline.RoundingType = ESlateBrushRoundingType::FixedRadius;
		if (bSimple)
		{
			Outline.Color = FSlateColor(V.BorderColour);
			Outline.Width = FMath::Max(V.BorderWidth, 0.f);
		}
		else
		{
			Outline.Color = FSlateColor(FLinearColor::Transparent);
			Outline.Width = 0.f;
		}
		B.OutlineSettings = Outline;
		Bg->SetBrush(B);
	}

	// Frame (moldura material) visível só no modo Frame.
	if (BorderFx)
	{
		BorderFx->SetVisibility(bFrame ? ESlateVisibility::HitTestInvisible : ESlateVisibility::Collapsed);
	}
	// Cantos visíveis só no modo Corners.
	if (Corners)
	{
		Corners->SetVisibility(bCorners ? ESlateVisibility::HitTestInvisible : ESlateVisibility::Collapsed);
	}

	if (ButtonText)
	{
		ButtonText->SetColorAndOpacity(FSlateColor(V.TextColour));
	}

	SetRenderScale(FVector2D(V.Scale, V.Scale));
	SetRenderOpacity(V.RenderOpacity);
}

void UUIButton_Base::EnsureMID()
{
	// Prioriza o material do próprio brush (se o WBP definiu um); senão usa o
	// material default da classe. Idempotente por imagem.
	auto Ensure = [this](UImage* Img, UMaterialInterface* Fallback) -> UMaterialInstanceDynamic*
	{
		if (!Img) { return nullptr; }
		UMaterialInterface* Mat = Cast<UMaterialInterface>(Img->GetBrush().GetResourceObject());
		if (!Mat) { Mat = Fallback; }
		if (!Mat) { return nullptr; }
		UMaterialInstanceDynamic* MID = UMaterialInstanceDynamic::Create(Mat, this);
		Img->SetBrushFromMaterial(MID);
		return MID;
	};
	if (!BgMID)     { BgMID     = Ensure(Bg, BgMaterial); }
	if (!GlowMID)   { GlowMID   = Ensure(GlowFx, GlowMaterial); }
	if (!CornerMID) { CornerMID = Ensure(Corners, CornerMaterial); }
	if (!BorderMID) { BorderMID = Ensure(BorderFx, FrameMaterial); }
}

void UUIButton_Base::ApplyAspectToLayers() const
{
	// Cada camada usa o aspect da SUA geometry (Corners/BorderFx têm padding
	// próprio, então tamanho ≠ do botão). Sem isso, os traços esticam.
	auto SetAspect = [](UMaterialInstanceDynamic* MID, const UImage* Img)
	{
		if (!MID || !Img) { return; }
		const FVector2D S = Img->GetCachedGeometry().GetLocalSize();
		if (S.Y > 1.f)
		{
			MID->SetScalarParameterValue(P_AspectRatio, static_cast<float>(S.X / S.Y));
		}
	};
	SetAspect(CornerMID, Corners);
	SetAspect(BorderMID, BorderFx);
	SetAspect(GlowMID, GlowFx);
	// Glow (drop shadow): ancora o anel da sombra na borda do BOTÃO — fração =
	// margem (GlowExtent) / meia-altura do GlowFx. Assim a sombra esmaece pros
	// dois lados a partir da borda, sem aresta dura na borda externa do quad.
	if (GlowMID && GlowFx)
	{
		const FVector2D S = GlowFx->GetCachedGeometry().GetLocalSize();
		if (S.Y > 1.f)
		{
			// Edge = fração (em inY=min(V,1-V)) onde está a borda do botão = margem/altura.
			const float Edge = GlowExtent / static_cast<float>(S.Y);
			GlowMID->SetScalarParameterValue(P_GlowEdge, Edge);
			// Largura do esmaecimento ~ a margem (escala com o tamanho do botão).
			GlowMID->SetScalarParameterValue(P_GlowSoftness, Edge * 1.2f);
		}
	}
}

int32 UUIButton_Base::NativePaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry,
	const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements,
	int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
{
	// NativePaint roda no Designer E em runtime (o Tick não roda no Designer) —
	// é aqui que garantimos o aspect correto dos materiais em ambos.
	ApplyAspectToLayers();
	return Super::NativePaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements,
		LayerId, InWidgetStyle, bParentEnabled);
}

FUIButtonStateVisual UUIButton_Base::LerpVisual(const FUIButtonStateVisual& A, const FUIButtonStateVisual& B, float T)
{
	FUIButtonStateVisual R;
	R.TopColour     = FMath::Lerp(A.TopColour, B.TopColour, T);
	R.BottomColour  = FMath::Lerp(A.BottomColour, B.BottomColour, T);
	R.GlowColour    = FMath::Lerp(A.GlowColour, B.GlowColour, T);
	R.GlowIntensity = FMath::Lerp(A.GlowIntensity, B.GlowIntensity, T);
	R.BorderColour  = FMath::Lerp(A.BorderColour, B.BorderColour, T);
	R.BorderWidth   = FMath::Lerp(A.BorderWidth, B.BorderWidth, T);
	R.TextColour    = FMath::Lerp(A.TextColour, B.TextColour, T);
	R.Scale         = FMath::Lerp(A.Scale, B.Scale, T);
	R.RenderOpacity = FMath::Lerp(A.RenderOpacity, B.RenderOpacity, T);
	return R;
}

void UUIButton_Base::SetButtonText(const FText& InText)
{
	if (ButtonText)
	{
		ButtonText->SetText(InText);
	}
}

// === Acesso ao estilo ativo (DT_UI_Styles) — fase final ===

const FUIStyle* UUIButton_Base::GetActiveUIStyle(FName ThemeId) const
{
	const UDataTable* DT = LoadObject<UDataTable>(nullptr,
		TEXT("/Game/ZMMO/Data/UI/DT_UI_Styles.DT_UI_Styles"));
	if (!DT)
	{
		return nullptr;
	}

	if (const FUIStyleRow* Row = DT->FindRow<FUIStyleRow>(
		ThemeId, TEXT("UUIButton_Base::GetActiveUIStyle"), false))
	{
		return &Row->Style;
	}
	return nullptr;
}

const FUIStyleButton* UUIButton_Base::GetActiveButtonStyle(FName ThemeId) const
{
	const FUIStyle* UIStyle = GetActiveUIStyle(ThemeId);
	return UIStyle ? &UIStyle->Button : nullptr;
}
