package com.tiedup.remake.client.renderer;

import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import com.tiedup.remake.client.animation.render.RenderConstants;
import com.tiedup.remake.client.model.DamselModel;
import com.tiedup.remake.compat.wildfire.WildfireCompat;
import com.tiedup.remake.compat.wildfire.render.WildfireDamselLayer;
import com.tiedup.remake.entities.AbstractTiedUpNpc;
import net.minecraft.client.model.HumanoidArmorModel;
import net.minecraft.client.model.geom.ModelLayers;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.client.renderer.entity.HumanoidMobRenderer;
import net.minecraft.client.renderer.entity.layers.HumanoidArmorLayer;
import net.minecraft.client.renderer.entity.layers.ItemInHandLayer;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;

/**
 * Renderer for AbstractTiedUpNpc and all subtypes (Kidnapper, Elite, Archer, Merchant, Shiny).
 *
 * <p>Uses ISkinnedEntity interface for polymorphic texture lookup.
 * Each entity subclass overrides getSkinTexture() to return the appropriate texture.
 *
 * <p><b>Issue #19 fix:</b> Replaced 6+ instanceof checks with single interface call.
 */
@OnlyIn(Dist.CLIENT)
public class DamselRenderer
    extends HumanoidMobRenderer<AbstractTiedUpNpc, DamselModel>
{

    /**
     * Normal arms model (4px wide - Steve model).
     */
    private final DamselModel normalModel;

    /**
     * Slim arms model (3px wide - Alex model).
     */
    private final DamselModel slimModel;

    /**
     * Create renderer.
     *
     */
    public DamselRenderer(EntityRendererProvider.Context context) {
        super(
            context,
            new DamselModel(context.bakeLayer(ModelLayers.PLAYER), false),
            0.5f // Shadow radius
        );
        // Store both models for runtime swapping
        this.normalModel = this.getModel();
        this.slimModel = new DamselModel(
            context.bakeLayer(ModelLayers.PLAYER_SLIM),
            true
        );

        // Add armor render layer (renders equipped armor)
        this.addLayer(
            new HumanoidArmorLayer<>(
                this,
                new HumanoidArmorModel<>(
                    context.bakeLayer(ModelLayers.PLAYER_INNER_ARMOR)
                ),
                new HumanoidArmorModel<>(
                    context.bakeLayer(ModelLayers.PLAYER_OUTER_ARMOR)
                ),
                context.getModelManager()
            )
        );

        // Add item in hand layer (renders held items)
        this.addLayer(
            new ItemInHandLayer<>(this, context.getItemInHandRenderer())
        );

        // Add Wildfire breast layer BEFORE bondage (so bondage renders on top of breasts)
        if (WildfireCompat.isLoaded()) {
            this.addLayer(
                new WildfireDamselLayer<>(this, context.getModelSet())
            );
        }

        // Add V2 bondage render layer (GLB-based V2 equipment rendering)
        this.addLayer(
            new com.tiedup.remake.v2.bondage.client.V2BondageRenderLayer<>(this)
        );
    }

    /**
     * Render the entity.
     * Uses entity's hasSlimArms() for model selection.
     *
     * to ensure it happens after visibility resets.
     *
     * DOG pose: X rotation is applied in setupRotations() AFTER Y rotation,
     * so the "belly down" direction follows entity facing.
     * Head compensation is applied in DamselModel.setupAnim().
     * Body rotation smoothing is handled in AbstractTiedUpNpc.tick().
     */
    @Override
    public void render(
        AbstractTiedUpNpc entity,
        float entityYaw,
        float partialTicks,
        PoseStack poseStack,
        MultiBufferSource buffer,
        int packedLight
    ) {
        // Use entity's hasSlimArms() - each entity type overrides this appropriately
        boolean useSlim = entity.hasSlimArms();

        // Swap to appropriate model
        this.model = useSlim ? this.slimModel : this.normalModel;

        // Apply vertical offset for sitting/kneeling/dog poses
        // This ensures the model AND all layers (gag, blindfold, etc.) move together
        float verticalOffset = getVerticalOffset(entity);
        boolean pushedPose = false;
        if (verticalOffset != 0) {
            poseStack.pushPose();
            pushedPose = true;
            // Convert from model units (16 = 1 block) to render units
            poseStack.translate(0, verticalOffset / 16.0, 0);
        }

        // Call parent render
        // Note: Wearer layer hiding happens in DamselModel.setupAnim()
        super.render(
            entity,
            entityYaw,
            partialTicks,
            poseStack,
            buffer,
            packedLight
        );

        if (pushedPose) {
            poseStack.popPose();
        }
    }

    /**
     * Get vertical offset for sitting/kneeling/dog poses.
     * Returns offset in model units (16 units = 1 block).
     *
     * @param entity The entity to check
     * @return Vertical offset (negative = down)
     */
    private float getVerticalOffset(AbstractTiedUpNpc entity) {
        if (entity.isSitting()) {
            return RenderConstants.DAMSEL_SIT_OFFSET;
        } else if (entity.isKneeling()) {
            return RenderConstants.DAMSEL_KNEEL_OFFSET;
        } else if (entity.isDogPose()) {
            return RenderConstants.DAMSEL_DOG_OFFSET;
        }
        return 0;
    }

    /**
     * Get the texture location based on entity type.
     *
     * <p>Issue #19 fix: Uses ISkinnedEntity interface instead of instanceof cascade.
     * Each entity subclass implements getSkinTexture() to return appropriate texture.
     */
    @Override
    public ResourceLocation getTextureLocation(AbstractTiedUpNpc entity) {
        // ISkinnedEntity provides polymorphic skin texture lookup
        // Each entity type (Damsel, Kidnapper, Elite, Archer, Merchant, Shiny)
        // overrides getSkinTexture() to return the correct texture
        return entity.getSkinTexture();
    }

    /**
     * Apply scale transformation.
     */
    @Override
    protected void scale(
        AbstractTiedUpNpc entity,
        PoseStack poseStack,
        float partialTick
    ) {
        poseStack.scale(0.9375f, 0.9375f, 0.9375f);
    }

    /**
     * Setup rotations for the entity.
     *
     * DOG pose: After Y rotation is applied by parent, add X rotation
     * to make the body horizontal. This is applied in entity-local space,
     * so the "belly down" direction follows the entity's facing.
     */
    @Override
    protected void setupRotations(
        AbstractTiedUpNpc entity,
        PoseStack poseStack,
        float ageInTicks,
        float rotationYaw,
        float partialTicks
    ) {
        // Call parent to apply Y rotation (body facing)
        super.setupRotations(
            entity,
            poseStack,
            ageInTicks,
            rotationYaw,
            partialTicks
        );

        // DOG pose: Apply X rotation to make body horizontal
        // This happens AFTER Y rotation, so it's in entity-local space
        if (entity.isDogPose()) {
            // Rotate -90° on X axis around the model's pivot point
            // Pivot at waist height (12 model units = 0.75 blocks up from feet)
            poseStack.translate(0, 0.75, 0);
            poseStack.mulPose(Axis.XP.rotationDegrees(-90));
            poseStack.translate(0, -0.75, 0);
        }
    }
}
