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

#pragma once

#include "CoreMinimal.h"
#include "CharacterAppearanceTypes.generated.h"

class USkeletalMesh;
class UTexture2D;

/** Les deux silhouettes livrees par le pack. */
UENUM(BlueprintType)
enum class ECharacterBodyType : uint8
{
	BodyA UMETA(DisplayName = "Body A"),
	BodyB UMETA(DisplayName = "Body B"),

	/** Sentinelle, comme ECraftingStation::Count. Jamais une valeur valide. */
	Count UMETA(Hidden)
};

/**
 * Les emplacements de pieces montees sur le corps.
 *
 * Sert d'index dans le tableau de composants du monteur : une seule fonction de
 * montage plutot qu'un membre nomme par piece, et ajouter une categorie ne
 * demande qu'une entree ici.
 */
UENUM(BlueprintType)
enum class ECharacterPartCategory : uint8
{
	Head,
	Hair,
	Eyebrows,
	Beard,
	Mustache,

	Count UMETA(Hidden)
};

/**
 * Une entree du catalogue : la piece que le joueur choisit dans une categorie.
 */
USTRUCT(BlueprintType)
struct FCharacterPartEntry
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Part")
	TObjectPtr<USkeletalMesh> Mesh;

	/**
	 * La vignette du bouton, dans la grille de l'ecran de customisation.
	 * Meme type que l'icone d'UItemDataAsset, pour que les deux grilles du jeu
	 * se dessinent de la meme facon.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Part")
	TObjectPtr<UTexture2D> Icon;
};

/**
 * L'apparence d'un personnage, en 11 octets.
 *
 * Des INDEX et jamais des chemins d'assets, pour deux raisons qui vont dans le
 * meme sens : un chemin coute une chaine de caracteres a chaque replication,
 * alors qu'un index tient dans un octet ; et un index se VALIDE contre le
 * catalogue, quand un chemin envoye par un client pourrait designer n'importe
 * quel asset du jeu.
 *
 * Les couleurs suivent la meme logique : un index dans la palette, pas un
 * FLinearColor. C'est ce qui permet a la struct entiere de tenir dans une
 * poignee d'octets, et de se sauvegarder telle quelle dans le .ini de
 * USurvivalUserSettings.
 */
USTRUCT(BlueprintType)
struct FCharacterAppearance
{
	GENERATED_BODY()

	/** Index qui signifie « aucune piece » -- barbe rasee, crane nu. */
	static constexpr uint8 NoPart = 255;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Appearance")
	ECharacterBodyType BodyType = ECharacterBodyType::BodyA;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Appearance")
	uint8 HeadIndex = 0;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Appearance")
	uint8 HairIndex = 0;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Appearance")
	uint8 EyebrowsIndex = 0;

	/** NoPart par defaut : un visage glabre est le point de depart naturel. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Appearance")
	uint8 BeardIndex = NoPart;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Appearance")
	uint8 MustacheIndex = NoPart;

	/**
	 * Les yeux ne sont pas un mesh mais un MATERIAU pose sur un slot du mesh de
	 * tete -- le pack ne livre aucun `SKM_Eyes`. D'ou une categorie a part,
	 * absente d'ECharacterPartCategory qui ne parle que de composants.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Appearance")
	uint8 EyesIndex = 0;

	// --------------------------------------------------------------
	// Couleurs : un index dans la palette correspondante du catalogue.
	// Un bouton de l'ecran = un index, sans reglage intermediaire.
	// --------------------------------------------------------------

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Appearance|Colors")
	uint8 SkinColorIndex = 0;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Appearance|Colors")
	uint8 HairColorIndex = 0;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Appearance|Colors")
	uint8 EyeColorIndex = 0;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Appearance|Colors")
	uint8 UnderwearColorIndex = 0;

	bool operator==(const FCharacterAppearance& Other) const
	{
		return BodyType == Other.BodyType
			&& HeadIndex == Other.HeadIndex
			&& HairIndex == Other.HairIndex
			&& EyebrowsIndex == Other.EyebrowsIndex
			&& BeardIndex == Other.BeardIndex
			&& MustacheIndex == Other.MustacheIndex
			&& EyesIndex == Other.EyesIndex
			&& SkinColorIndex == Other.SkinColorIndex
			&& HairColorIndex == Other.HairColorIndex
			&& EyeColorIndex == Other.EyeColorIndex
			&& UnderwearColorIndex == Other.UnderwearColorIndex;
	}

	bool operator!=(const FCharacterAppearance& Other) const { return !(*this == Other); }
};
