using System.Collections.Generic;
using UnityEngine;
///
/// Registre des matériaux INSTANCIÉS pour un objet précis.
///
/// Le problème : partout dans le projet on écrit `renderer.material = Instantiate(_G.QUELQUECHOSE)`
/// ou `renderer.sharedMaterial = Instantiate(MAT)`. Une copie créée par `Instantiate` n'est PAS
/// suivie par Unity — contrairement à celle que crée le getter `renderer.material`. Détruire le
/// GameObject ne la libère donc pas : elle reste en mémoire jusqu'à la fin de la session.
/// Ces appels sont sur des chemins PAR FACE (chaque face de caisson, chaque porte, chaque panneau),
/// et l'IA détruit puis reconstruit toute la cuisine à chaque génération : la mémoire montait en
/// escalier jusqu'au `abortOnCannotGrowMemory` du build web.
///
/// Le principe retenu : chaque copie est rattachée à l'objet qui la porte, et libérée par son
/// OnDestroy. Aucun balayage global, donc aucun risque pour les matériaux PARTAGÉS chargés par
/// Resources.Load (_G.INV, _G.GYPSE, _G.ST...) ni pour ceux que UIT_MATERIAL met en cache dans
/// _C.ColorInScene : ils ne passent jamais par ici.
///
/// Couvre aussi les suppressions manuelles, pas seulement DeleteAllCab : la libération suit la
/// destruction de l'objet, d'où qu'elle vienne.
///
public class MAT_TRASH : MonoBehaviour
{
readonly List _owned = new();
/// Nombre total de matériaux libérés depuis le lancement. Trace de vérification.
public static int FreedCount { get; private set; }
/// Nombre de matériaux actuellement enregistrés, tous objets confondus.
public static int TrackedCount { get; private set; }
///
/// Remplace `Instantiate(source)` : crée la copie ET la rattache à 'owner', qui la libérera
/// en mourant. Retourne null si la source est nulle, comme Instantiate.
///
public static Material New(Material source, GameObject owner)
{
if (source == null) return null;
Material m = Instantiate(source);
Register(m, owner);
return m;
}
///
/// Surcharges pour un porteur donné sous forme de Component — Transform, Renderer, MonoBehaviour.
/// Les appelants écrivent souvent `foreach (Transform child in ...)` : sans elles il faudrait
/// ajouter `.gameObject` à chaque site.
///
public static Material New(Material source, Component owner)
=> New(source, owner != null ? owner.gameObject : null);
public static void Register(Material m, Component owner)
=> Register(m, owner != null ? owner.gameObject : null);
/// Rattache une copie déjà créée à son objet porteur.
public static void Register(Material m, GameObject owner)
{
if (m == null || owner == null) return;
// JAMAIS un materiau PARTAGE. _G.MaterialOnScene est le cache que sert Get.MaterialInScene :
// la meme instance est rendue a tous les appelants. Le detruire avec un seul de ses
// porteurs laisse tous les autres en ROSE. Le registre ne doit suivre que des copies.
if (_G.MaterialOnScene != null)
{
foreach (var kv in _G.MaterialOnScene)
{
if (kv.Value != m) continue;
Debug.LogWarning("[MAT_TRASH] " + m.name + " est un materiau PARTAGE ("
+ kv.Key + ") -> non enregistre, il aurait ete detruit avec "
+ owner.name + ".");
return;
}
}
if (!owner.TryGetComponent(out MAT_TRASH trash))
{
trash = owner.AddComponent();
trash.hideFlags = HideFlags.HideInInspector;
}
trash._owned.Add(m);
TrackedCount++;
}
void OnDestroy()
{
for (int i = 0; i < _owned.Count; i++)
{
if (_owned[i] == null) continue;
Destroy(_owned[i]);
FreedCount++;
TrackedCount--;
}
_owned.Clear();
}
}