Helper/ClothingModelSuffixStripper.cs
namespace Skateboard;

public sealed class ClothingModelSuffixStripper : Component
{
	[Property] public Dresser Dresser { get; set; }
	private bool _applied;

	protected override void OnStart()
	{
		Dresser ??= Components.Get<Dresser>();
	}

	protected override void OnUpdate()
	{
		if ( Dresser is null )
			return;

		if ( Dresser.IsDressing )
		{
			_applied = false;
			return;
		}

		if ( _applied )
			return;

		OverrideClothingModels();
	}

	private void OverrideClothingModels()
	{
		var target = Dresser.BodyTarget;
		if ( target is null || !target.IsValid() )
			return;

		var clothingEntries = GetClothingEntries();
		if ( clothingEntries is null || clothingEntries.Count == 0 )
		{
			_applied = true;
			return;
		}

		var foundClothingObject = false;
		foreach ( var entry in clothingEntries )
		{
			var clothing = entry?.Clothing;
			if ( clothing is null )
				continue;

			var modelPath = clothing.Model;
			if ( string.IsNullOrWhiteSpace( modelPath ) )
				continue;

			var clothingObject = FindClothingObject( target.GameObject, clothing.ResourceName );
			if ( clothingObject is null )
				continue;

			foundClothingObject = true;

			var renderer = clothingObject.Components.Get<SkinnedModelRenderer>();
			if ( renderer is null )
				continue;

			var model = Model.Load( modelPath );
			if ( model is null || !model.IsValid() || model.IsError )
				continue;

			if ( renderer.Model == model )
				continue;

			renderer.Model = model;
		}

		if ( foundClothingObject )
			_applied = true;
	}

	private List<ClothingContainer.ClothingEntry> GetClothingEntries()
	{
		switch ( Dresser.Source )
		{
			case Dresser.ClothingSource.Manual:
				return Dresser.Clothing;
			case Dresser.ClothingSource.OwnerConnection:
				return GetOwnerClothingEntries();
			case Dresser.ClothingSource.LocalUser:
				return ClothingContainer.CreateFromLocalUser()?.Clothing;
		}

		return null;
	}

	private List<ClothingContainer.ClothingEntry> GetOwnerClothingEntries()
	{
		var owner = Dresser.Network?.Owner;
		if ( owner is null )
			return null;

		var clothingData = owner.GetUserData( "avatar" );
		if ( string.IsNullOrEmpty( clothingData ) )
			return null;

		var container = new ClothingContainer();
		container.Deserialize( clothingData );
		container.Normalize();
		return container.Clothing;
	}

	private static GameObject FindClothingObject( GameObject root, string resourceName )
	{
		if ( root is null || string.IsNullOrWhiteSpace( resourceName ) )
			return null;

		var name = $"Clothing - {resourceName}";
		foreach ( var child in root.Children )
		{
			if ( child.Name == name )
				return child;
		}

		foreach ( var child in root.GetAllObjects( false ) )
		{
			if ( child.Name == name )
				return child;
		}

		return null;
	}
}