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

#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "Containers/Ticker.h"
#include "ConfirmDialogWidget.generated.h"

class UButton;
class UTextBlock;

DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnConfirmDialogConfirmed);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnConfirmDialogCancelled);

/**
 * Boite de dialogue "es-tu sur ?", volontairement generique.
 *
 * Elle ne sait pas ce qu'elle confirme : elle pose la question et previent via
 * ses delegues. C'est l'appelant qui decide de l'action. Ainsi la meme boite
 * servira pour quitter, effacer une sauvegarde ou abandonner une partie.
 *
 * Classe Abstract : on cree un WBP_ConfirmDialog qui en herite.
 */
UCLASS(Abstract)
class EMBERWILD_API UConfirmDialogWidget : public UUserWidget
{
	GENERATED_BODY()

public:
	UConfirmDialogWidget(const FObjectInitializer& ObjectInitializer);

	virtual void NativeConstruct() override;
	virtual void NativeDestruct() override;
	virtual FReply NativeOnKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent) override;

	UPROPERTY(BlueprintAssignable, Category = "Dialogue")
	FOnConfirmDialogConfirmed OnConfirmed;

	UPROPERTY(BlueprintAssignable, Category = "Dialogue")
	FOnConfirmDialogCancelled OnCancelled;

	/**
	 * Affiche la boite et lui donne le focus clavier.
	 *
	 * Passe par ici et jamais par SetVisibility directement : sans le focus,
	 * la boite ne recevrait aucune touche et Echap resterait sans effet.
	 */
	UFUNCTION(BlueprintCallable, Category = "Dialogue")
	void Open(const FText& InMessage);

	/**
	 * Meme chose, mais avec un retour automatique passe le delai.
	 *
	 * @param InMessageFormat doit contenir {0}, remplace par les secondes
	 *                        restantes. Exemple : "Garder ce mode ?\nRetour
	 *                        automatique dans {0} s."
	 *
	 * A l'expiration, OnCancelled est diffuse -- comme si le joueur avait
	 * refuse. C'est le seul filet quand la question porte sur un changement qui
	 * peut rendre l'ecran illisible.
	 */
	UFUNCTION(BlueprintCallable, Category = "Dialogue")
	void OpenWithCountdown(const FText& InMessageFormat, float Seconds);

	/** Referme la boite. Ne previent personne : l'appelant sait ce qu'il fait. */
	UFUNCTION(BlueprintCallable, Category = "Dialogue")
	void Close();

protected:
	/** Echap equivaut a cliquer Annuler. Decoche pour une question incontournable. */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Dialogue")
	bool bCancelOnEscape = true;

	/**
	 * Entree equivaut a cliquer Confirmer.
	 *
	 * Desactive par defaut, volontairement : sur une question destructrice, un
	 * joueur qui martele Entree validerait sans avoir lu.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Dialogue")
	bool bConfirmOnEnter = false;

	UPROPERTY(meta = (BindWidget))
	TObjectPtr<UButton> ConfirmButton;

	UPROPERTY(meta = (BindWidget))
	TObjectPtr<UButton> CancelButton;

	/** Optionnel : une boite dont le texte est fige dans le Blueprint reste valide. */
	UPROPERTY(meta = (BindWidgetOptional))
	TObjectPtr<UTextBlock> MessageText;

private:
	UFUNCTION()
	void HandleConfirmClicked();

	UFUNCTION()
	void HandleCancelClicked();

	/** Un tick par frame tant que le rebours tourne. Renvoie false pour s'arreter. */
	bool TickCountdown(float DeltaTime);

	void StopCountdown();

	void RefreshCountdownText();

	/**
	 * Le rebours passe par le ticker du moteur et non par un timer du monde.
	 *
	 * Deux raisons : l'ecran de reglages s'ouvre depuis le menu de PAUSE, ou les
	 * timers du monde sont geles -- le rebours ne repartirait jamais ; et un
	 * changement de resolution rate doit se defaire meme si le jeu bloque.
	 */
	FTSTicker::FDelegateHandle CountdownTickerHandle;

	FText CountdownMessageFormat;

	float CountdownRemaining = 0.f;

	/** Evite de reecrire le texte 60 fois par seconde pour rien. */
	int32 LastDisplayedSeconds = -1;
};
