/*
 * 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.patch;

import javax.annotation.Nullable;

import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.Vec3;

import com.tiedup.remake.rig.TiedUpRigConstants;
import com.tiedup.remake.rig.anim.Animator;
import com.tiedup.remake.rig.anim.LivingMotion;
import com.tiedup.remake.rig.anim.LivingMotions;
import com.tiedup.remake.rig.anim.Pose;
import com.tiedup.remake.rig.anim.client.ClientAnimator;
import com.tiedup.remake.rig.anim.types.DynamicAnimation;
import com.tiedup.remake.rig.armature.Armature;

/**
 * RIG Phase 2 — patch de capability attaché à un {@link LivingEntity}. Porte
 * l'{@link Animator} (initialisé eager via {@link TiedUpRigConstants#ANIMATOR_PROVIDER}
 * dans {@link #onConstructed(LivingEntity)}) et expose {@link #getArmature()}
 * (abstract — subclass fournit son biped armature via {@code TiedUpRigRegistry}).
 *
 * <p><b>Pattern init EF-style (voir EF LivingEntityPatch:146-156)</b> :
 * <ol>
 *   <li>{@code onConstructed} fait super.onConstructed puis construit l'animator
 *       via le factory client/server split</li>
 *   <li>{@link #initAnimator(Animator)} permet aux subclasses de registrer leurs
 *       {@code LivingMotion → StaticAnimation} avant que l'animator commence à
 *       tourner</li>
 *   <li>{@link Animator#postInit()} poste l'event {@code InitAnimatorEvent}
 *       pour les extensions tierces</li>
 * </ol>
 * </p>
 */
public abstract class LivingEntityPatch<T extends LivingEntity> extends EntityPatch<T> {
	public LivingMotion currentLivingMotion = LivingMotions.IDLE;
	public LivingMotion currentCompositeMotion = LivingMotions.IDLE;

	/** Animator initialisé dans {@link #onConstructed(LivingEntity)}, non-null après. */
	protected Animator animator;

	public abstract void updateMotion(boolean considerInaction);

	public abstract Armature getArmature();

	@Override
	public void onConstructed(T entity) {
		super.onConstructed(entity);
		this.animator = TiedUpRigConstants.ANIMATOR_PROVIDER.apply(this);
		this.initAnimator(this.animator);
		this.animator.postInit();
	}

	/**
	 * Hook pour subclasses : bind des {@code LivingMotion → StaticAnimation}
	 * sur l'animator avant qu'il ne commence à tourner. Default no-op.
	 *
	 * <p>Exemple subclass :</p>
	 * <pre>
	 * protected void initAnimator(Animator a) {
	 *     a.addLivingAnimation(LivingMotions.IDLE, TiedUpAnimationRegistry.CONTEXT_STAND_IDLE);
	 * }
	 * </pre>
	 */
	protected void initAnimator(Animator animator) {
		// no-op par défaut
	}

	public Animator getAnimator() {
		return this.animator;
	}

	@Nullable
	public ClientAnimator getClientAnimator() {
		return this.animator instanceof ClientAnimator ca ? ca : null;
	}

	public LivingMotion getCurrentLivingMotion() {
		return this.currentLivingMotion;
	}

	public LivingMotion getCurrentCompositeMotion() {
		return this.currentCompositeMotion;
	}

	@Nullable
	public LivingEntity getTarget() {
		return this.original != null ? this.original.getLastHurtMob() : null;
	}

	@Nullable
	public LivingEntity getGrapplingTarget() {
		return null;
	}

	public float getReach() {
		return 1.0F;
	}

	public float getReach(InteractionHand hand) {
		return getReach();
	}

	public float getYRot() {
		return this.original.getYRot();
	}

	public void setYRot(float yRot) {
		this.original.setYRot(yRot);
	}

	public float getYRotLimit() {
		return 180.0F;
	}

	public float getYRotDeltaTo(Entity entity) {
		Vec3 delta = entity.position().subtract(this.original.position());
		float yaw = (float) Math.toDegrees(Math.atan2(delta.z, delta.x)) - 90.0F;
		return net.minecraft.util.Mth.wrapDegrees(yaw - this.original.getYRot());
	}

	public void poseTick(DynamicAnimation animation, Pose pose, float elapsedTime, float partialTick) {
	}

	public void updateEntityState() {
	}

	@Override
	public boolean overrideRender() {
		return false;
	}
}
