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


#include "ItemDataAsset.h"

#include "Components/StaticMeshComponent.h"

#if WITH_EDITOR
#include "Misc/DataValidation.h"
#endif

#define LOCTEXT_NAMESPACE "ItemData"

namespace
{
	/**
	 * Pose un mesh et son eventuel materiau de surcharge sur un composant.
	 *
	 * Prefixe du fichier oblige : le build unity concatene les .cpp du module,
	 * un namespace anonyme n'isole rien.
	 */
	void ItemDataApplyMeshAndMaterial(UStaticMeshComponent* Component, UStaticMesh* Mesh, UMaterialInterface* Material)
	{
		// OverrideMaterials appartient au COMPOSANT, pas au mesh : il survit donc
		// a un changement d'asset. Sans ce vidage, un objet sans surcharge monte
		// apres un objet qui en avait une garderait le materiau du precedent --
		// le meme piege que les meshes de corps de la customisation. Il ne se
		// voit que sur un composant REUTILISE, donc jamais sur un pickup fraichement
		// engendre, et systematiquement sur l'objet en main ou sur un grill.
		Component->EmptyOverrideMaterials();

		Component->SetStaticMesh(Mesh);

		// SetMaterial(0, nullptr) ne "retire" pas le materiau, il pose un slot vide
		// et le mesh devient gris damier. Sans override, on ne touche a rien et le
		// mesh garde le sien.
		if (Material)
		{
			Component->SetMaterial(0, Material);
		}
	}
}

void UItemDataAsset::ApplyWorldVisualsTo(UStaticMeshComponent* Component) const
{
	if (!Component)
	{
		return;
	}

	ItemDataApplyMeshAndMaterial(Component, WorldMesh, WorldMeshMaterial);
}

void UItemDataAsset::ApplyHeldVisualsTo(UStaticMeshComponent* Component) const
{
	if (!Component)
	{
		return;
	}

	// Le materiau de surcharge est celui du monde, sans equivalent "en main" :
	// il sert a distinguer un cru d'un cuit, et cette distinction vaut aussi
	// dans la main. Un second champ n'aurait fait que se desynchroniser.
	ItemDataApplyMeshAndMaterial(Component, GetHeldMesh(), WorldMeshMaterial);
}

#if WITH_EDITOR
EDataValidationResult UItemDataAsset::IsDataValid(FDataValidationContext& Context) const
{
	EDataValidationResult Result = Super::IsDataValid(Context);

	if (bCanBeCooked && !CookedResult)
	{
		Context.AddError(LOCTEXT("CookNoResult", "'Can Be Cooked' is checked but no 'Cooked Result' is assigned: this item would sit on the fire forever without ever changing."));
		Result = EDataValidationResult::Invalid;
	}

	if (bCanBurn && !BurntResult)
	{
		Context.AddError(LOCTEXT("BurnNoResult", "'Can Burn' is checked but no 'Burnt Result' is assigned."));
		Result = EDataValidationResult::Invalid;
	}

	// Une chaine qui reboucle sur elle-meme ne plante pas, elle tourne : la
	// station re-arme un timer identique a chaque etape et le joueur voit un
	// objet qui ne finit jamais de cuire. Impossible a diagnostiquer en jeu.
	if (CookedResult == this)
	{
		Context.AddError(LOCTEXT("CookSelfResult", "'Cooked Result' points back to this asset: the item would cook into itself, endlessly."));
		Result = EDataValidationResult::Invalid;
	}

	if (BurntResult == this)
	{
		Context.AddError(LOCTEXT("BurnSelfResult", "'Burnt Result' points back to this asset: the item would burn into itself, endlessly."));
		Result = EDataValidationResult::Invalid;
	}

	// Un objet a charges ne s'empile pas (voir GetEffectiveMaxStack). Le poser
	// sur le feu reste possible, mais la charge entamee suit l'exemplaire et le
	// resultat en herite : autant que ce soit un choix, pas une surprise.
	if (MaxUses > 1 && bCanBeCooked)
	{
		Context.AddWarning(LOCTEXT("CookableWithCharges", "This item has multiple uses AND can be cooked. The remaining charges follow the item through cooking, which is rarely what you want for food."));
	}

	// Le symptome serait une main vide sans le moindre message : le composant
	// aurait bien monte son mesh, avec un asset nul.
	if (bShowInHand && !GetHeldMesh())
	{
		Context.AddError(LOCTEXT("HeldNoMesh", "'Show In Hand' is checked but neither 'Held Mesh' nor 'World Mesh' is assigned: nothing would appear in the player's hand."));
		Result = EDataValidationResult::Invalid;
	}

	// Un outil invisible se remarque tout de suite en jeu, mais on cherche la
	// cause du mauvais cote -- dans le composant, dans le socket, dans le rig --
	// avant de penser a une case a cocher.
	if (IsTool() && !bShowInHand)
	{
		Context.AddWarning(LOCTEXT("ToolNotShownInHand", "This item is a tool but 'Show In Hand' is unchecked: the player will harvest with an empty hand."));
	}

	return Result;
}
#endif

#undef LOCTEXT_NAMESPACE
