// Fill out your copyright notice in the Description page of Project Settings.


#include "ConfirmDialogWidget.h"

#include "Components/Button.h"
#include "Components/TextBlock.h"
#include "InputCoreTypes.h"

UConfirmDialogWidget::UConfirmDialogWidget(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	// Sans ce drapeau le widget ne peut pas prendre le focus clavier, et
	// NativeOnKeyDown ne serait jamais appele. Il ne se change qu'a la
	// construction, d'ou sa presence ici et pas dans Open().
	SetIsFocusable(true);
}

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

	if (ConfirmButton)
	{
		ConfirmButton->OnClicked.AddDynamic(this, &UConfirmDialogWidget::HandleConfirmClicked);
	}

	if (CancelButton)
	{
		CancelButton->OnClicked.AddDynamic(this, &UConfirmDialogWidget::HandleCancelClicked);
	}
}

void UConfirmDialogWidget::Open(const FText& InMessage)
{
	// Une ouverture simple annule un rebours laisse par la precedente.
	StopCountdown();

	if (MessageText)
	{
		MessageText->SetText(InMessage);
	}

	SetVisibility(ESlateVisibility::Visible);

	// Le focus doit etre repris a CHAQUE ouverture : le refermer le rend au
	// menu, et une boite visible mais sans focus ignorerait le clavier.
	SetKeyboardFocus();
}

void UConfirmDialogWidget::OpenWithCountdown(const FText& InMessageFormat, float Seconds)
{
	StopCountdown();

	CountdownMessageFormat = InMessageFormat;
	CountdownRemaining = FMath::Max(Seconds, 1.f);
	LastDisplayedSeconds = -1;

	RefreshCountdownText();

	SetVisibility(ESlateVisibility::Visible);
	SetKeyboardFocus();

	CountdownTickerHandle = FTSTicker::GetCoreTicker().AddTicker(
		FTickerDelegate::CreateUObject(this, &UConfirmDialogWidget::TickCountdown));
}

void UConfirmDialogWidget::Close()
{
	StopCountdown();

	SetVisibility(ESlateVisibility::Collapsed);
}

void UConfirmDialogWidget::StopCountdown()
{
	if (CountdownTickerHandle.IsValid())
	{
		FTSTicker::GetCoreTicker().RemoveTicker(CountdownTickerHandle);
		CountdownTickerHandle.Reset();
	}
}

void UConfirmDialogWidget::RefreshCountdownText()
{
	if (!MessageText)
	{
		return;
	}

	// CeilToInt : on affiche "10" pendant la premiere seconde et "1" pendant la
	// derniere. Avec un arrondi classique on afficherait "0" une demi-seconde
	// avant que ca se declenche, ce qui donne l'impression d'un bug.
	const int32 Seconds = FMath::CeilToInt(CountdownRemaining);
	if (Seconds == LastDisplayedSeconds)
	{
		return;
	}

	LastDisplayedSeconds = Seconds;
	MessageText->SetText(FText::Format(CountdownMessageFormat, FText::AsNumber(Seconds)));
}

bool UConfirmDialogWidget::TickCountdown(float DeltaTime)
{
	CountdownRemaining -= DeltaTime;

	if (CountdownRemaining > 0.f)
	{
		RefreshCountdownText();

		// true : on veut etre rappele a la frame suivante.
		return true;
	}

	// On coupe le ticker AVANT de diffuser : un abonne qui rouvre une boite
	// dans son handler doit pouvoir relancer un rebours.
	StopCountdown();
	OnCancelled.Broadcast();

	return false;
}

FReply UConfirmDialogWidget::NativeOnKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent)
{
	const FKey Key = InKeyEvent.GetKey();

	// La touche "retour" de la manette en plus d'Echap : Slate y fait pointer le
	// bouton retour de la plateforme (B sur Xbox, Rond sur PlayStation). Une
	// seule ligne et le clavier comme la manette sont couverts, sans avoir a
	// enumerer les touches de chaque constructeur.
	//
	// GetVirtualKey() et non la constante EKeys::Virtual_Back, depreciee en 5.8 :
	// la resolution se fait desormais a l'appel, donc a chaud selon la manette
	// reellement branchee.
	if (bCancelOnEscape && (Key == EKeys::Escape || Key == EKeys::Virtual_Gamepad_Back.GetVirtualKey()))
	{
		StopCountdown();
		OnCancelled.Broadcast();

		// Handled et non Unhandled : sans ca la touche continue de remonter et
		// serait consommee une seconde fois par ce qui se trouve derriere.
		return FReply::Handled();
	}

	if (bConfirmOnEnter && (Key == EKeys::Enter || Key == EKeys::Virtual_Gamepad_Accept.GetVirtualKey()))
	{
		StopCountdown();
		OnConfirmed.Broadcast();
		return FReply::Handled();
	}

	return Super::NativeOnKeyDown(InGeometry, InKeyEvent);
}

void UConfirmDialogWidget::HandleConfirmClicked()
{
	// Le joueur a repondu : le rebours n'a plus lieu d'etre, et le laisser
	// tourner ferait arriver un OnCancelled apres coup.
	StopCountdown();

	OnConfirmed.Broadcast();
}

void UConfirmDialogWidget::HandleCancelClicked()
{
	StopCountdown();

	OnCancelled.Broadcast();
}

void UConfirmDialogWidget::NativeDestruct()
{
	// Le ticker du moteur survit au widget : sans ce retrait, il rappellerait
	// une methode sur un objet detruit.
	StopCountdown();

	if (ConfirmButton)
	{
		ConfirmButton->OnClicked.RemoveDynamic(this, &UConfirmDialogWidget::HandleConfirmClicked);
	}

	if (CancelButton)
	{
		CancelButton->OnClicked.RemoveDynamic(this, &UConfirmDialogWidget::HandleCancelClicked);
	}

	Super::NativeDestruct();
}
