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

#pragma once

#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "CharacterAppearanceTypes.h"
#include "CharacterPartsDataAsset.generated.h"

class UAnimInstance;
class UMaterialInterface;
class USkeletalMesh;
class UTexture2D;

/**
 * Tout ce qui compose une silhouette : ses meshes de corps, son AnimBP, et les
 * pieces disponibles pour chaque categorie.
 */
USTRUCT(BlueprintType)
struct FCharacterBodySet
{
	GENERATED_BODY()

	/** La vignette du bouton de silhouette, meme role que celle d'une piece. */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Body")
	TObjectPtr<UTexture2D> Icon;

	/**
	 * Les morceaux du corps, dans l'ordre. Le PREMIER est le porteur : c'est lui
	 * qui va sur le composant Mesh du Character et qui joue l'animation, les
	 * suivants deviennent des composants enfants en leader pose.
	 *
	 * Une LISTE alors qu'il n'y a qu'une case aujourd'hui, et ce n'est pas de la
	 * sur-conception : le pack livre exprès `SKM_BodyA_torso`, `_arms`,
	 * `_legs_01/02/03`, `_hands`, `_feet`, precisement pour qu'une armure puisse
	 * eteindre les segments qu'elle recouvre. Le jour venu, la case 0 deviendra
	 * `SKM_BodyA_empty` -- invisible, porteur d'animation -- et les huit segments
	 * suivront derriere, SANS que ce code change. Un TObjectPtr unique, lui,
	 * imposerait de tout reprendre au premier plastron.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Body")
	TArray<TObjectPtr<USkeletalMesh>> BodyMeshes;

	/** L'AnimBP du porteur. Il change avec la silhouette, d'ou sa place ici. */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Body")
	TSubclassOf<UAnimInstance> AnimClass;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parts")
	TArray<FCharacterPartEntry> Heads;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parts")
	TArray<FCharacterPartEntry> Hairstyles;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parts")
	TArray<FCharacterPartEntry> Eyebrows;

	/** Vide pour une silhouette qui n'en porte pas -- le pack n'en donne qu'a A. */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parts")
	TArray<FCharacterPartEntry> Beards;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parts")
	TArray<FCharacterPartEntry> Mustaches;

	/**
	 * Fragment cherche dans le NOM des slots de materiau du mesh de tete pour y
	 * trouver celui des yeux.
	 *
	 * Les yeux ne sont pas un mesh : le pack les livre comme un element du mesh
	 * de tete. Il ne suffit donc pas de poser un parametre de couleur, il faut
	 * REMPLACER le materiau de ce slot -- encore faut-il savoir lequel.
	 *
	 * Un fragment de nom et pas un index, parce qu'un index est faux deux fois :
	 * il oblige a le verifier a la main dans l'editeur, et il suppose surtout
	 * que les six tetes rangent leurs slots dans le MEME ordre. Les slots du
	 * pack s'appellent `M_Eye_A_01`, `M_Eye`, `M_Eyes_02`... tous contiennent
	 * « Eye », aucun slot de peau ne le contient.
	 *
	 * Si le fragment ne trouve rien, le materiau n'est PAS pose et un
	 * avertissement part dans le log. C'est volontaire : poser l'oeil sur un
	 * slot au hasard repeindrait le visage entier, un symptome qu'on met bien
	 * plus longtemps a diagnostiquer qu'une ligne de log.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parts")
	FName EyeMaterialSlotHint = TEXT("Eye");
};

/**
 * Le catalogue : la table de correspondance entre les index d'une
 * FCharacterAppearance et les assets reels.
 *
 * Un seul asset a remplir, et le SEUL endroit a toucher pour ajouter une
 * coiffure. C'est aussi lui qui borne ce qu'un client peut demander : une
 * apparence arrivee du reseau est passee a Sanitize(), qui ramene tout index
 * hors bornes a une valeur sure.
 */
UCLASS(BlueprintType)
class EMBERWILD_API UCharacterPartsDataAsset : public UPrimaryDataAsset
{
	GENERATED_BODY()

public:
	/**
	 * Une entree par valeur d'ECharacterBodyType, dans l'ordre de l'enum.
	 * L'ordre EST le contrat : c'est lui qui donne son sens a BodyType.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog")
	TArray<FCharacterBodySet> BodySets;

	// ------------------------------------------------------------------
	// Palettes : une couleur = un bouton a l'ecran, sans reglage entre les
	// deux. L'ordre EST l'ordre d'affichage, et l'index qui part sur le reseau.
	//
	// Partagees par les deux silhouettes : deux nuanciers de peau a tenir a
	// jour divergeraient au premier ajout.
	// ------------------------------------------------------------------

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog|Palettes")
	TArray<FLinearColor> SkinColors;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog|Palettes")
	TArray<FLinearColor> HairColors;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog|Palettes")
	TArray<FLinearColor> EyeColors;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog|Palettes")
	TArray<FLinearColor> UnderwearColors;

	/** Les materiaux d'iris, un par forme d'oeil proposee. */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog|Palettes")
	TArray<TObjectPtr<UMaterialInterface>> EyeMaterials;

	// ------------------------------------------------------------------
	// Noms des parametres des materiaux du pack.
	//
	// Ici et pas en dur dans le C++ : ils appartiennent aux assets, pas au
	// code. Changer de pack de personnages ne doit demander qu'un nouveau
	// catalogue -- et ces noms contiennent des ESPACES, ce qu'on n'invente
	// pas de memoire.
	// ------------------------------------------------------------------

	/** `M_Body_Base`. */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog|Material Parameters")
	FName SkinColorParameter = TEXT("Skin color");

	/**
	 * `M_Head_Base` -- le meme role que ci-dessus, mais l'autre master, et
	 * surtout un NOM DIFFERENT : `Skin` cote tete, `Skin color` cote corps.
	 *
	 * Le master contient bien la chaine « Skin Base color », mais ce n'est pas
	 * le nom du parametre expose -- verifie dans `MI_HeadA_01_a`, ou le chercher
	 * ne rend aucun resultat alors que `Skin` apparait dans le groupe
	 * « Base colors ». Morale : un nom de parametre se lit dans l'editeur du
	 * Material Instance, jamais dans les chaines du binaire du master.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog|Material Parameters")
	FName HeadSkinColorParameter = TEXT("Skin");

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog|Material Parameters")
	FName UnderwearColorParameter = TEXT("Underware color");

	/** `M_Hairstyle_Base`, parent des cheveux, barbes, moustaches ET sourcils. */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog|Material Parameters")
	FName HairColorParameter = TEXT("Color");

	/** `M_Eyes_Base`. */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog|Material Parameters")
	FName EyeColorParameter = TEXT("Eye color");

	// ------------------------------------------------------------------
	// Lecture
	// ------------------------------------------------------------------

	/** Le jeu de pieces d'une silhouette, ou nullptr si l'index est hors bornes. */
	const FCharacterBodySet* GetBodySet(ECharacterBodyType BodyType) const;

	/** La liste d'une categorie pour une silhouette, vide si rien n'est defini. */
	const TArray<FCharacterPartEntry>& GetParts(ECharacterBodyType BodyType, ECharacterPartCategory Category) const;

	/** Nombre d'options d'une categorie. Sert a borner l'UI comme la console. */
	UFUNCTION(BlueprintPure, Category = "Catalog")
	int32 GetPartCount(ECharacterBodyType BodyType, ECharacterPartCategory Category) const;

	/**
	 * Ramene une apparence dans les bornes du catalogue.
	 *
	 * Appelee sur le SERVEUR a la reception d'une apparence client, et au
	 * chargement des reglages -- un catalogue ampute entre deux versions du jeu
	 * laisserait sinon des index qui ne designent plus rien. Les pieces
	 * facultatives (barbe, moustache) retombent sur NoPart, les obligatoires
	 * sur 0 : mieux vaut la mauvaise tete que pas de tete.
	 */
	void Sanitize(FCharacterAppearance& InOut) const;
};
