package net.minecraft.client.renderer.entity;

import org.apache.logging.log4j.LogManager;
import net.minecraft.world.scores.Team;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.PlayerModelPart;
import net.minecraft.ChatFormatting;
import com.mojang.math.Vector3f;
import net.minecraft.client.renderer.texture.OverlayTexture;
import javax.annotation.Nullable;
import net.minecraft.resources.ResourceLocation;
import java.util.Iterator;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.Direction;
import net.minecraft.world.entity.player.Player;
import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.Pose;
import net.minecraft.util.Mth;
import net.minecraft.client.renderer.MultiBufferSource;
import com.mojang.blaze3d.vertex.PoseStack;
import com.google.common.collect.Lists;
import net.minecraft.client.renderer.entity.layers.RenderLayer;
import java.util.List;
import org.apache.logging.log4j.Logger;
import net.minecraft.client.model.EntityModel;
import net.minecraft.world.entity.LivingEntity;

public abstract class LivingEntityRenderer<T extends LivingEntity, M extends EntityModel<T>> extends EntityRenderer<T> implements RenderLayerParent<T, M> {
    private static final Logger LOGGER;
    protected M model;
    protected final List<RenderLayer<T, M>> layers;
    
    public LivingEntityRenderer(final EntityRenderDispatcher dvf, final M dla, final float float3) {
        super(dvf);
        this.layers = Lists.newArrayList();
        this.model = dla;
        this.shadowRadius = float3;
    }
    
    protected final boolean addLayer(final RenderLayer<T, M> dzd) {
        return this.layers.add(dzd);
    }
    
    @Override
    public M getModel() {
        return this.model;
    }
    
    @Override
    public void render(final T akz, final float float2, final float float3, final PoseStack cyl, final MultiBufferSource dqt, final int integer) {
        cyl.pushPose();
        this.model.attackTime = this.getAttackAnim(akz, float3);
        this.model.riding = akz.isPassenger();
        this.model.young = akz.isBaby();
        float float4 = Mth.rotLerp(float3, akz.yBodyRotO, akz.yBodyRot);
        final float float5 = Mth.rotLerp(float3, akz.yHeadRotO, akz.yHeadRot);
        float float6 = float5 - float4;
        if (akz.isPassenger() && akz.getVehicle() instanceof LivingEntity) {
            final LivingEntity akz2 = (LivingEntity)akz.getVehicle();
            float4 = Mth.rotLerp(float3, akz2.yBodyRotO, akz2.yBodyRot);
            float6 = float5 - float4;
            float float7 = Mth.wrapDegrees(float6);
            if (float7 < -85.0f) {
                float7 = -85.0f;
            }
            if (float7 >= 85.0f) {
                float7 = 85.0f;
            }
            float4 = float5 - float7;
            if (float7 * float7 > 2500.0f) {
                float4 += float7 * 0.2f;
            }
            float6 = float5 - float4;
        }
        final float float8 = Mth.lerp(float3, akz.xRotO, akz.xRot);
        if (akz.getPose() == Pose.SLEEPING) {
            final Direction fp12 = akz.getBedOrientation();
            if (fp12 != null) {
                final float float9 = akz.getEyeHeight(Pose.STANDING) - 0.1f;
                cyl.translate(-fp12.getStepX() * float9, 0.0, -fp12.getStepZ() * float9);
            }
        }
        float float7 = this.getBob(akz, float3);
        this.setupRotations(akz, cyl, float7, float4, float3);
        cyl.scale(-1.0f, -1.0f, 1.0f);
        this.scale(akz, cyl, float3);
        cyl.translate(0.0, -1.5010000467300415, 0.0);
        float float9 = 0.0f;
        float float10 = 0.0f;
        if (!akz.isPassenger() && akz.isAlive()) {
            float9 = Mth.lerp(float3, akz.animationSpeedOld, akz.animationSpeed);
            float10 = akz.animationPosition - akz.animationSpeed * (1.0f - float3);
            if (akz.isBaby()) {
                float10 *= 3.0f;
            }
            if (float9 > 1.0f) {
                float9 = 1.0f;
            }
        }
        this.model.prepareMobModel(akz, float10, float9, float3);
        this.model.setupAnim(akz, float10, float9, float7, float6, float8);
        final boolean boolean15 = this.isBodyVisible(akz);
        final boolean boolean16 = !boolean15 && !akz.isInvisibleTo(Minecraft.getInstance().player);
        final RenderType drb17 = this.getRenderType(akz, boolean15, boolean16);
        if (drb17 != null) {
            final VertexConsumer cyo18 = dqt.getBuffer(drb17);
            final int integer2 = getOverlayCoords(akz, this.getWhiteOverlayProgress(akz, float3));
            this.model.renderToBuffer(cyl, cyo18, integer, integer2, 1.0f, 1.0f, 1.0f, boolean16 ? 0.15f : 1.0f);
        }
        if (!akz.isSpectator()) {
            for (final RenderLayer<T, M> dzd19 : this.layers) {
                dzd19.render(cyl, dqt, integer, akz, float10, float9, float3, float7, float6, float8);
            }
        }
        cyl.popPose();
        super.render(akz, float2, float3, cyl, dqt, integer);
    }
    
    @Nullable
    protected RenderType getRenderType(final T akz, final boolean boolean2, final boolean boolean3) {
        final ResourceLocation sm5 = this.getTextureLocation(akz);
        if (boolean3) {
            return RenderType.entityTranslucent(sm5);
        }
        if (boolean2) {
            return this.model.renderType(sm5);
        }
        if (akz.isGlowing()) {
            return RenderType.outline(sm5);
        }
        return null;
    }
    
    public static int getOverlayCoords(final LivingEntity akz, final float float2) {
        return OverlayTexture.pack(OverlayTexture.u(float2), OverlayTexture.v(akz.hurtTime > 0 || akz.deathTime > 0));
    }
    
    protected boolean isBodyVisible(final T akz) {
        return !akz.isInvisible();
    }
    
    private static float sleepDirectionToRotation(final Direction fp) {
        switch (fp) {
            case SOUTH: {
                return 90.0f;
            }
            case WEST: {
                return 0.0f;
            }
            case NORTH: {
                return 270.0f;
            }
            case EAST: {
                return 180.0f;
            }
            default: {
                return 0.0f;
            }
        }
    }
    
    protected void setupRotations(final T akz, final PoseStack cyl, final float float3, final float float4, final float float5) {
        final Pose alj7 = akz.getPose();
        if (alj7 != Pose.SLEEPING) {
            cyl.mulPose(Vector3f.YP.rotationDegrees(180.0f - float4));
        }
        if (akz.deathTime > 0) {
            float float6 = (akz.deathTime + float5 - 1.0f) / 20.0f * 1.6f;
            float6 = Mth.sqrt(float6);
            if (float6 > 1.0f) {
                float6 = 1.0f;
            }
            cyl.mulPose(Vector3f.ZP.rotationDegrees(float6 * this.getFlipDegrees(akz)));
        }
        else if (akz.isAutoSpinAttack()) {
            cyl.mulPose(Vector3f.XP.rotationDegrees(-90.0f - akz.xRot));
            cyl.mulPose(Vector3f.YP.rotationDegrees((akz.tickCount + float5) * -75.0f));
        }
        else if (alj7 == Pose.SLEEPING) {
            final Direction fp8 = akz.getBedOrientation();
            final float float7 = (fp8 != null) ? sleepDirectionToRotation(fp8) : float4;
            cyl.mulPose(Vector3f.YP.rotationDegrees(float7));
            cyl.mulPose(Vector3f.ZP.rotationDegrees(this.getFlipDegrees(akz)));
            cyl.mulPose(Vector3f.YP.rotationDegrees(270.0f));
        }
        else if (akz.hasCustomName() || akz instanceof Player) {
            final String string8 = ChatFormatting.stripFormatting(akz.getName().getString());
            if (("Dinnerbone".equals(string8) || "Grumm".equals(string8)) && (!(akz instanceof Player) || ((Player)akz).isModelPartShown(PlayerModelPart.CAPE))) {
                cyl.translate(0.0, akz.getBbHeight() + 0.1f, 0.0);
                cyl.mulPose(Vector3f.ZP.rotationDegrees(180.0f));
            }
        }
    }
    
    protected float getAttackAnim(final T akz, final float float2) {
        return akz.getAttackAnim(float2);
    }
    
    protected float getBob(final T akz, final float float2) {
        return akz.tickCount + float2;
    }
    
    protected float getFlipDegrees(final T akz) {
        return 90.0f;
    }
    
    protected float getWhiteOverlayProgress(final T akz, final float float2) {
        return 0.0f;
    }
    
    protected void scale(final T akz, final PoseStack cyl, final float float3) {
    }
    
    @Override
    protected boolean shouldShowName(final T akz) {
        final double double3 = this.entityRenderDispatcher.distanceToSqr(akz);
        final float float5 = akz.isDiscrete() ? 32.0f : 64.0f;
        if (double3 >= float5 * float5) {
            return false;
        }
        final Minecraft dbn6 = Minecraft.getInstance();
        final LocalPlayer dqb7 = dbn6.player;
        final boolean boolean8 = !akz.isInvisibleTo(dqb7);
        if (akz != dqb7) {
            final Team cwn9 = akz.getTeam();
            final Team cwn10 = dqb7.getTeam();
            if (cwn9 != null) {
                final Team.Visibility b11 = cwn9.getNameTagVisibility();
                switch (b11) {
                    case ALWAYS: {
                        return boolean8;
                    }
                    case NEVER: {
                        return false;
                    }
                    case HIDE_FOR_OTHER_TEAMS: {
                        return (cwn10 == null) ? boolean8 : (cwn9.isAlliedTo(cwn10) && (cwn9.canSeeFriendlyInvisibles() || boolean8));
                    }
                    case HIDE_FOR_OWN_TEAM: {
                        return (cwn10 == null) ? boolean8 : (!cwn9.isAlliedTo(cwn10) && boolean8);
                    }
                    default: {
                        return true;
                    }
                }
            }
        }
        return Minecraft.renderNames() && akz != dbn6.getCameraEntity() && boolean8 && !akz.isVehicle();
    }
    
    static {
        LOGGER = LogManager.getLogger();
    }
}
