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

#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "CharacterAppearanceTypes.h"
#include "CharacterPreviewActor.h"
#include "CharacterCustomizationWidget.generated.h"

class ACharacterPreviewActor;
class UButton;
class UCharacterPartsDataAsset;
class UCustomizationCategoryWidget;
class UCustomizationOptionWidget;
class UPanelWidget;

DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnCustomizationConfirmed);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnCustomizationCancelled);

/**
 * Ce que le joueur peut regler, dans l'ordre d'affichage de la colonne.
 *
 * Pieces et couleurs sont dans le MEME enum : ce sont onze rangees de la meme
 * colonne, et les distinguer par deux listes obligerait a dessiner deux fois la
 * meme chose. La difference ne vit que dans BuildOptions, qui remplit la grille
 * de vignettes ou de pastilles selon le cas.
 */
UENUM(BlueprintType)
enum class ECustomizationCategory : uint8
{
	Body,
	Head,
	Hair,
	Eyebrows,
	Beard,
	Mustache,
	Eyes,
	SkinColor,
	HairColor,
	EyeColor,
	UnderwearColor,

	Count UMETA(Hidden)
};

/**
 * L'ecran de customisation : la colonne de reglages a gauche, le personnage a
 * droite.
 *
 * Le widget ne monte rien lui-meme -- il pousse une FCharacterAppearance sur
 * ACharacterPreviewActor et lit le catalogue pour savoir quoi proposer. C'est
 * le meme partage des roles que partout ailleurs dans le projet : l'interface
 * affiche et demande, le systeme decide.
 *
 * Classe Abstract : on cree un WBP_CharacterCustomization qui en herite.
 */
UCLASS(Abstract)
class EMBERWILD_API UCharacterCustomizationWidget : public UUserWidget
{
	GENERATED_BODY()

public:
	UCharacterCustomizationWidget(const FObjectInitializer& ObjectInitializer);

	virtual void NativeOnInitialized() override;
	virtual void NativeConstruct() override;

	/**
	 * Echap = Retour, comme le bouton.
	 *
	 * Obligatoire ici et pas seulement confortable : dans la map du menu, le
	 * controller ne binde AUCUNE action d'input. Tout Echap qui fonctionne passe
	 * donc par le NativeOnKeyDown d'un widget -- il n'y a pas d'autre chemin.
	 */
	virtual FReply NativeOnKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent) override;

	/** Le joueur valide : l'ecran a fini, l'apparence est dans GetAppearance(). */
	UPROPERTY(BlueprintAssignable, Category = "Customization")
	FOnCustomizationConfirmed OnConfirmed;

	/** Le joueur renonce : rien n'a ete valide, on rend la main au menu. */
	UPROPERTY(BlueprintAssignable, Category = "Customization")
	FOnCustomizationCancelled OnCancelled;

	/**
	 * Branche l'ecran sur son mannequin et son point de depart.
	 *
	 * A appeler AVANT d'ajouter le widget au viewport : NativeConstruct
	 * reconstruit les listes, et il les batirait vides sans catalogue.
	 *
	 * Surtout PAS nommee Initialize : UUserWidget en a deja une, virtuelle, et
	 * une surcharge de signature differente la masquerait au lieu de la
	 * surcharger (C4263/C4264). Meme famille de piege que les membres nommes
	 * Player ou Slot dans une classe derivee du moteur.
	 */
	UFUNCTION(BlueprintCallable, Category = "Customization")
	void SetupCustomization(ACharacterPreviewActor* InPreview, const FCharacterAppearance& InAppearance);

	UFUNCTION(BlueprintPure, Category = "Customization")
	const FCharacterAppearance& GetAppearance() const { return Appearance; }

protected:
	/**
	 * Le catalogue. Assigne-lui le meme DA_CharacterParts que BP_FpsPlayer.
	 *
	 * Sur le widget et pas pris sur le mannequin : l'ecran doit pouvoir batir
	 * ses listes meme si le mannequin manque encore, et un catalogue different
	 * des deux cotes se verrait immediatement a l'ecran.
	 */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Customization")
	TObjectPtr<UCharacterPartsDataAsset> Catalog;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Customization")
	TSubclassOf<UCustomizationCategoryWidget> CategoryWidgetClass;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Customization")
	TSubclassOf<UCustomizationOptionWidget> OptionWidgetClass;

	/** La colonne des categories. Un Vertical Box nomme exactement ainsi. */
	UPROPERTY(meta = (BindWidget))
	TObjectPtr<UPanelWidget> CategoryList;

	/** La grille d'options. Un Wrap Box va tres bien. */
	UPROPERTY(meta = (BindWidget))
	TObjectPtr<UPanelWidget> OptionGrid;

	/** Valide et lance la partie. */
	UPROPERTY(meta = (BindWidget))
	TObjectPtr<UButton> PlayButton;

	/** Retour au menu principal, sans rien valider. */
	UPROPERTY(meta = (BindWidget))
	TObjectPtr<UButton> BackButton;

	// ------------------------------------------------------------------
	// Rotation du personnage au clic-glisser
	// ------------------------------------------------------------------

	virtual FReply NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;
	virtual FReply NativeOnMouseButtonUp(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;
	virtual FReply NativeOnMouseMove(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;

private:
	/** Reconstruit la colonne de gauche. Une seule fois, les categories sont fixes. */
	void BuildCategories();

	/** Reconstruit la grille selon la categorie active. */
	void BuildOptions();

	/** Repose l'apparence sur le mannequin et rafraichit la selection affichee. */
	void RefreshPreview();

	/** Encadre la case qui porte le choix courant, efface les autres. */
	void RefreshSelection();

	/** L'index courant dans la categorie active, pour l'encadrement. */
	int32 GetCurrentIndexForCategory(ECustomizationCategory Category) const;

	/** Ecrit un choix dans l'apparence. Le seul endroit qui la modifie. */
	void SetIndexForCategory(ECustomizationCategory Category, int32 NewIndex);

	/** Combien d'options a proposer, catalogue en main. */
	int32 GetOptionCountForCategory(ECustomizationCategory Category) const;

	UFUNCTION()
	void HandleCategoryClicked(int32 CategoryIndex);

	UFUNCTION()
	void HandleOptionClicked(int32 OptionIndex);

	UFUNCTION()
	void HandlePlayClicked();

	UFUNCTION()
	void HandleBackClicked();

	UPROPERTY(Transient)
	TObjectPtr<ACharacterPreviewActor> Preview;

	UPROPERTY(Transient)
	TArray<TObjectPtr<UCustomizationCategoryWidget>> CategoryWidgets;

	UPROPERTY(Transient)
	TArray<TObjectPtr<UCustomizationOptionWidget>> OptionWidgets;

	FCharacterAppearance Appearance;

	ECustomizationCategory ActiveCategory = ECustomizationCategory::Body;

	/** Vrai entre l'appui et le relachement, pendant qu'on fait tourner. */
	bool bDragging = false;
};
