/*
 * Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
 * by the Epic Fight Team, licensed under GPLv3.
 * Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
 */

package com.tiedup.remake.rig.render;

import net.minecraft.client.model.PlayerModel;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.client.renderer.entity.layers.PlayerItemInHandLayer;
import net.minecraft.client.renderer.entity.player.PlayerRenderer;
import net.minecraft.world.entity.EntityType;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;

import com.tiedup.remake.rig.asset.AssetAccessor;
import com.tiedup.remake.rig.mesh.HumanoidMesh;
import com.tiedup.remake.rig.mesh.Meshes;
import com.tiedup.remake.rig.mesh.SkinnedMesh;
import com.tiedup.remake.rig.patch.ClientPlayerPatch;

/**
 * Renderer RIG patché pour les joueurs (local + remote).
 *
 * <p>Dispatch slim vs default via {@link #getMeshProvider} — suit la règle
 * vanilla {@code entity.getModelName().equals("slim")} → Alex mesh, sinon
 * Steve/biped mesh.</p>
 *
 * <p>Override de {@link #prepareModel} pour propager les flags de visibilité
 * des {@link PlayerModel#head head / hat / jacket / sleeves / pants} vers les
 * {@link HumanoidMesh} parts correspondants — nécessaire pour que les
 * toggles client MC ("Cape", "Left Sleeve" etc.) fonctionnent avec le rendu
 * RIG.</p>
 *
 * <p><b>Layers skipés vs EF</b> (Phase 2.5, à rework Phase 3) :</p>
 * <ul>
 *   <li>{@code ArrowLayer} — flèches plantées dans le player (V3-REW-09)</li>
 *   <li>{@code BeeStingerLayer} — dards d'abeille plantés (V3-REW-09)</li>
 *   <li>{@code CapeLayer} — cape vanilla (V3-REW-08, compat skin layers)</li>
 * </ul>
 *
 * <p><b>Layers mappés Phase 2.5</b> :</p>
 * <ul>
 *   <li>{@code HumanoidArmorLayer} → {@code PatchedArmorLayer} (hérité de PHumanoidRenderer)</li>
 *   <li>{@code ItemInHandLayer} → {@code PatchedItemInHandLayer} (hérité de PHumanoidRenderer)</li>
 *   <li>{@link PlayerItemInHandLayer} → {@code PatchedItemInHandLayer} (ajouté ici,
 *       P1-RISK-003 review Phase 2.5 : dispatch par classe stricte, PlayerItemInHandLayer
 *       extends ItemInHandLayer mais {@code .getClass()} diffère → sans mapping explicite,
 *       double-draw item quand Phase 3 câblera le vrai rendu)</li>
 * </ul>
 *
 * <p>Fork conceptuel de {@code yesman.epicfight.client.renderer.patched.entity.PPlayerRenderer}
 * (EF 60 LOC) — signatures identiques mais sans patched layers combat EF.</p>
 */
@OnlyIn(Dist.CLIENT)
public class TiedUpPlayerRenderer extends PHumanoidRenderer<AbstractClientPlayer, ClientPlayerPatch<AbstractClientPlayer>, PlayerModel<AbstractClientPlayer>, PlayerRenderer, HumanoidMesh> {

	public TiedUpPlayerRenderer(EntityRendererProvider.Context context, EntityType<?> entityType) {
		super(Meshes.BIPED, context, entityType);

		// P1-RISK-003 (review Phase 2.5) : PlayerItemInHandLayer extends ItemInHandLayer
		// mais le dispatch `patchedLayers.containsKey(layer.getClass())` fait un
		// match strict de classe — sans mapping explicite, le stub parent n'est
		// pas reconnu et le layer vanilla continue de dessiner (double-draw
		// quand Phase 3 activera le vrai rendu item). On réutilise le même stub
		// no-op PatchedItemInHandLayer.
		//
		// Note : pas de problème Phase 2.5 tant que PatchedItemInHandLayer est
		// no-op complet (aucun draw dans les 2 layers). Préventif pour Phase 3+.
		this.addPatchedLayer(PlayerItemInHandLayer.class, new PatchedItemInHandLayer<>());
	}

	@Override
	protected void prepareModel(HumanoidMesh mesh, AbstractClientPlayer entity, ClientPlayerPatch<AbstractClientPlayer> entitypatch, PlayerRenderer renderer) {
		super.prepareModel(mesh, entity, entitypatch, renderer);

		// Renderer vanilla : lit les player options (modelParts toggle UI)
		// pour mettre à jour model.{head,hat,jacket,leftArm,...}.visible.
		renderer.setModelProperties(entity);
		PlayerModel<AbstractClientPlayer> model = renderer.getModel();

		// Propage vers le mesh RIG : chaque part peut être hidden indépendamment.
		// HumanoidMesh peut avoir des parts null (legacy biped sans sleeves) —
		// on protège, sinon NPE au runtime sur les meshes partiels.
		setHiddenSafe(mesh.head, !model.head.visible);
		setHiddenSafe(mesh.hat, !model.hat.visible);
		setHiddenSafe(mesh.jacket, !model.jacket.visible);
		setHiddenSafe(mesh.torso, !model.body.visible);
		setHiddenSafe(mesh.leftArm, !model.leftArm.visible);
		setHiddenSafe(mesh.leftLeg, !model.leftLeg.visible);
		setHiddenSafe(mesh.leftPants, !model.leftPants.visible);
		setHiddenSafe(mesh.leftSleeve, !model.leftSleeve.visible);
		setHiddenSafe(mesh.rightArm, !model.rightArm.visible);
		setHiddenSafe(mesh.rightLeg, !model.rightLeg.visible);
		setHiddenSafe(mesh.rightPants, !model.rightPants.visible);
		setHiddenSafe(mesh.rightSleeve, !model.rightSleeve.visible);
	}

	private static void setHiddenSafe(SkinnedMesh.SkinnedMeshPart part, boolean hidden) {
		if (part != null) {
			part.setHidden(hidden);
		}
	}

	@Override
	public AssetAccessor<HumanoidMesh> getMeshProvider(ClientPlayerPatch<AbstractClientPlayer> entitypatch) {
		return entitypatch.getOriginal().getModelName().equals("slim") ? Meshes.ALEX : Meshes.BIPED;
	}

	@Override
	public AssetAccessor<HumanoidMesh> getDefaultMesh() {
		return Meshes.BIPED;
	}
}
