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

#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "SettingsMenuWidget.generated.h"

class UButton;
class UConfirmDialogWidget;
class UKeyCaptureWidget;
class USettingsTabWidget;
class UWidgetSwitcher;

DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnSettingsCloseRequested);

/** Reponse a une demande de confirmation. bConfirmed est faux sur un refus ou une expiration. */
DECLARE_DYNAMIC_DELEGATE_OneParam(FOnSettingsConfirmationResult, bool, bConfirmed);

/** Touche capturee. Invalide si le joueur a annule avec Echap. */
DECLARE_DYNAMIC_DELEGATE_OneParam(FOnSettingsKeyCaptureResult, FKey, CapturedKey);

/**
 * L'ecran de reglages : une barre d'onglets, un WidgetSwitcher, Retour et
 * Reinitialiser.
 *
 * Il ne sait PAS qui l'a ouvert. Il demande a se fermer via OnCloseRequested et
 * c'est l'appelant -- menu principal ou menu de pause -- qui decide de ce que
 * "Retour" veut dire. Sans ca il faudrait un ecran par contexte.
 *
 * Il ne connait pas davantage le contenu de ses onglets : il appelle
 * RefreshFromSettings et ResetToDefaults sur USettingsTabWidget. Ajouter
 * l'onglet Graphismes ne demandera aucune ligne ici.
 *
 * Classe Abstract : on cree un WBP_Settings qui en herite.
 */
UCLASS(Abstract)
class EMBERWILD_API USettingsMenuWidget : public UUserWidget
{
	GENERATED_BODY()

public:
	USettingsMenuWidget(const FObjectInitializer& ObjectInitializer);

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

	UPROPERTY(BlueprintAssignable, Category = "Reglages")
	FOnSettingsCloseRequested OnCloseRequested;

	/**
	 * Affiche l'ecran, rafraichit l'onglet visible et prend le focus clavier.
	 *
	 * Passe par ici et jamais par SetVisibility : sans le focus, Echap ne
	 * refermerait pas l'ecran.
	 */
	UFUNCTION(BlueprintCallable, Category = "Reglages")
	void Open();

	/** Masque l'ecran et ecrit le fichier de reglages. */
	UFUNCTION(BlueprintCallable, Category = "Reglages")
	void Close();

	/**
	 * ZOrder de la boite de confirmation, impose par celui qui ouvre l'ecran.
	 *
	 * L'ecran ne le choisit PAS lui-meme : il est ouvert depuis deux contextes
	 * dont les empilements n'ont rien a voir (50 au menu principal, 150 en jeu).
	 * Un numero code en dur ici obligerait le widget a savoir qui l'affiche,
	 * exactement ce qu'on evite avec OnCloseRequested. A appeler AVANT Open().
	 */
	UFUNCTION(BlueprintCallable, Category = "Reglages")
	void SetDialogZOrder(int32 InZOrder) { DialogZOrder = InZOrder; }

	/**
	 * Pose une question au joueur pour le compte d'un onglet.
	 *
	 * L'ecran est le seul a posseder une boite : un onglet qui creerait la
	 * sienne devrait a son tour se demander a quel ZOrder la poser, et on
	 * repartirait pour un tour.
	 *
	 * @param MessageOrFormat  texte simple, ou format contenant {0} si
	 *                         CountdownSeconds est superieur a zero.
	 * @param CountdownSeconds 0 pour une question sans limite de temps.
	 * @return false si aucune boite n'est configuree -- l'appelant doit alors
	 *         decider seul, sa reponse n'arrivera jamais.
	 */
	bool RequestConfirmation(const FText& MessageOrFormat, float CountdownSeconds, const FOnSettingsConfirmationResult& OnResult);

	/**
	 * Ouvre le voile de capture de touche pour le compte d'un onglet.
	 *
	 * Meme raison que la boite : un voile pose DANS l'onglet serait clippe a la
	 * zone du WidgetSwitcher au lieu de couvrir l'ecran, et devrait a son tour
	 * se trouver un ZOrder.
	 *
	 * @return false si aucun voile n'est configure -- la reponse n'arrivera
	 *         jamais, l'appelant doit renoncer.
	 */
	bool RequestKeyCapture(const FText& ActionLabel, const FOnSettingsKeyCaptureResult& OnResult);

	/**
	 * Bascule le voile deja ouvert sur la question de conflit.
	 *
	 * Le voile RESTE ouvert entre la capture et la reponse : c'est le meme geste
	 * pour le joueur, il n'a pas a le voir disparaitre puis revenir.
	 */
	UFUNCTION(BlueprintCallable, Category = "Reglages")
	void ShowKeyCaptureConflict(const FText& Message, const FOnSettingsConfirmationResult& OnAnswer);

	/** Referme le voile. A appeler quand le remappage est termine ou abandonne. */
	UFUNCTION(BlueprintCallable, Category = "Reglages")
	void CloseKeyCapture();

protected:
	UPROPERTY(meta = (BindWidget))
	TObjectPtr<UWidgetSwitcher> TabSwitcher;

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

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

	/**
	 * Les cinq onglets sont OBLIGATOIRES : un WBP_Settings sans eux ne compile pas.
	 *
	 * BindWidgetOptional echoue en silence -- un nom mal orthographie donnait un
	 * bouton parfaitement inerte, sans le moindre message. Pour un element dont
	 * l'absence n'a aucun sens, on veut l'erreur a la compilation du Blueprint.
	 */
	UPROPERTY(meta = (BindWidget))
	TObjectPtr<UButton> GameplayTabButton;

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

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

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

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

	/**
	 * Ton WBP_ConfirmDialog, a renseigner dans les Class Defaults de
	 * WBP_Settings. Laisse-le vide et "Reinitialiser" agit sans rien demander.
	 */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Reglages")
	TSubclassOf<UConfirmDialogWidget> ResetDialogClass;

	/** La question posee avant de reinitialiser l'onglet visible. */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Reglages")
	FText ResetDialogMessage;

	/** Ton WBP_KeyCapture. Sans lui, l'onglet Touches ne peut rien remapper. */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Reglages")
	TSubclassOf<UKeyCaptureWidget> KeyCaptureClass;

	UPROPERTY(Transient, BlueprintReadOnly, Category = "Reglages")
	TObjectPtr<UConfirmDialogWidget> ResetDialog;

	UPROPERTY(Transient, BlueprintReadOnly, Category = "Reglages")
	TObjectPtr<UKeyCaptureWidget> KeyCapture;

private:
	UFUNCTION()
	void HandleBackClicked();

	UFUNCTION()
	void HandleResetClicked();

	/** Reponse a la question du bouton Reinitialiser. */
	UFUNCTION()
	void HandleResetAnswered(bool bConfirmed);

	UFUNCTION()
	void HandleDialogConfirmed();

	UFUNCTION()
	void HandleDialogCancelled();

	/** Referme la boite et rend la reponse a qui a pose la question. */
	void FinishConfirmation(bool bConfirmed);

	/** Cree la boite au premier besoin. Renvoie nullptr si aucune classe n'est assignee. */
	UConfirmDialogWidget* EnsureDialog();

	UFUNCTION()
	void HandleGameplayTabClicked();

	UFUNCTION()
	void HandleDisplayTabClicked();

	UFUNCTION()
	void HandleGraphicsTabClicked();

	UFUNCTION()
	void HandleControlsTabClicked();

	UFUNCTION()
	void HandleAudioTabClicked();

	UFUNCTION()
	void HandleKeyCaptured(FKey CapturedKey);

	UFUNCTION()
	void HandleKeyCaptureCancelled();

	UFUNCTION()
	void HandleKeyConflictAnswered(bool bConfirmed);

	/** A qui rendre la reponse du conflit affiche dans le voile. */
	FOnSettingsConfirmationResult PendingConflictAnswer;

	/** Change de page et rafraichit la nouvelle. Ignore un index inexistant. */
	void ShowTab(int32 Index);

	/** La page actuellement affichee, ou nullptr si le switcher est vide. */
	USettingsTabWidget* GetActiveTab() const;

	/** Grise les boutons dont la page n'existe pas encore. */
	void UpdateTabButtonStates();

	/** A qui rendre la reponse de la boite ouverte. Vide quand rien n'est pose. */
	FOnSettingsConfirmationResult PendingConfirmation;

	/** A qui rendre la touche capturee. Vide quand le voile est ferme. */
	FOnSettingsKeyCaptureResult PendingKeyCapture;

	bool bResetDialogOpen = false;

	bool bKeyCaptureOpen = false;

	/** Valeur de repli si l'appelant oublie SetDialogZOrder : devant le fondu exclu. */
	int32 DialogZOrder = 500;
};
