package me.km.entities;

import com.mojang.blaze3d.matrix.MatrixStack;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.Vector3f;
import net.minecraft.client.renderer.entity.EntityRendererManager;
import net.minecraft.client.renderer.entity.LivingRenderer;
import net.minecraft.client.renderer.entity.layers.ArrowLayer;
import net.minecraft.client.renderer.entity.layers.BeeStingerLayer;
import net.minecraft.client.renderer.entity.layers.BipedArmorLayer;
import net.minecraft.client.renderer.entity.layers.ElytraLayer;
import net.minecraft.client.renderer.entity.layers.HeadLayer;
import net.minecraft.client.renderer.entity.layers.HeldItemLayer;
import net.minecraft.client.renderer.entity.model.BipedModel;
import net.minecraft.client.renderer.entity.model.PlayerModel;
import net.minecraft.entity.Entity;
import net.minecraft.item.CrossbowItem;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.item.UseAction;
import net.minecraft.util.Hand;
import net.minecraft.util.HandSide;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;

@OnlyIn(Dist.CLIENT)
public class RenderHuman extends LivingRenderer<EntityHuman, ModelHuman> {
    private final ModelHuman steve;
    private final ModelHuman alex;

    public RenderHuman(EntityRendererManager renderManager) {
        super(renderManager, new ModelHuman(0.0f, false), 0.5F);
        this.addLayer(new BipedArmorLayer<>(this, new BipedModel(0.5F), new BipedModel(1.0F)));
        this.addLayer(new HeldItemLayer<>(this));
        this.addLayer(new ArrowLayer<>(this));
        this.addLayer(new HeadLayer<>(this));
        this.addLayer(new ElytraLayer<>(this));
        this.addLayer(new BeeStingerLayer<>(this));

        this.steve = this.getEntityModel();
        this.alex = new ModelHuman(0, true);
    }

    @Override
    public void render(EntityHuman ent, float entityYaw, float partialRenderTick, MatrixStack mStack, IRenderTypeBuffer buffer, int light) {
        this.setModelVisibilities(ent);
        if(ent.isSlim()) {
            this.entityModel = alex;
            super.render(ent, entityYaw, partialRenderTick, mStack, buffer, light);
            this.entityModel = steve;
        } else {
            super.render(ent, entityYaw, partialRenderTick, mStack, buffer, light);
        }
    }

    @Override
    public Vec3d getRenderOffset(EntityHuman ent, float p_225627_2_) {
        return ent.isCrouching() ? new Vec3d(0.0D, -0.125D, 0.0D) : super.getRenderOffset(ent, p_225627_2_);
    }

    private void setModelVisibilities(EntityHuman ent) {
        PlayerModel<EntityHuman> m = this.getEntityModel();
        ItemStack mainStack = ent.getHeldItemMainhand();
        ItemStack offStack = ent.getHeldItemOffhand();
        m.setVisible(true);
        m.bipedHeadwear.showModel = true;
        m.bipedBodyWear.showModel = true;
        m.bipedLeftLegwear.showModel = true;
        m.bipedRightLegwear.showModel = true;
        m.bipedLeftArmwear.showModel = true;
        m.bipedRightArmwear.showModel = true;
        m.isSneak = ent.isCrouching();
        BipedModel.ArmPose mainHand = getArmPose(ent, mainStack, offStack, Hand.MAIN_HAND);
        BipedModel.ArmPose offHand = getArmPose(ent, mainStack, offStack, Hand.OFF_HAND);
        if(ent.getPrimaryHand() == HandSide.RIGHT) {
            m.rightArmPose = mainHand;
            m.leftArmPose = offHand;
        } else {
            m.rightArmPose = offHand;
            m.leftArmPose = mainHand;
        }
    }

    private BipedModel.ArmPose getArmPose(EntityHuman ent, ItemStack mainStack, ItemStack offStack, Hand hand) {
        BipedModel.ArmPose pose = BipedModel.ArmPose.EMPTY;
        ItemStack itemstack = hand == Hand.MAIN_HAND ? mainStack : offStack;
        if(!itemstack.isEmpty()) {
            pose = BipedModel.ArmPose.ITEM;
            if(ent.getItemInUseCount() > 0) {
                UseAction useaction = itemstack.getUseAction();
                if(useaction == UseAction.BLOCK) {
                    pose = BipedModel.ArmPose.BLOCK;
                } else if(useaction == UseAction.BOW) {
                    pose = BipedModel.ArmPose.BOW_AND_ARROW;
                } else if(useaction == UseAction.SPEAR) {
                    pose = BipedModel.ArmPose.THROW_SPEAR;
                } else if(useaction == UseAction.CROSSBOW && hand == ent.getActiveHand()) {
                    pose = BipedModel.ArmPose.CROSSBOW_CHARGE;
                }
            } else {
                boolean flag3 = mainStack.getItem() == Items.CROSSBOW;
                boolean flag = CrossbowItem.isCharged(mainStack);
                boolean flag1 = offStack.getItem() == Items.CROSSBOW;
                boolean flag2 = CrossbowItem.isCharged(offStack);
                if(flag3 && flag) {
                    pose = BipedModel.ArmPose.CROSSBOW_HOLD;
                }

                if(flag1 && flag2 && mainStack.getItem().getUseAction(mainStack) == UseAction.NONE) {
                    pose = BipedModel.ArmPose.CROSSBOW_HOLD;
                }
            }
        }

        return pose;
    }

    @Override
    public ResourceLocation getEntityTexture(EntityHuman ent) {
        return ent.getTexture();
    }

    @Override
    protected void preRenderCallback(EntityHuman ent, MatrixStack mStack, float partialTickTime) {
        mStack.scale(0.9375F, 0.9375F, 0.9375F);
        float factor = ent.getScale();
        mStack.scale(factor, factor, factor);
    }

    @Override
    protected void applyRotations(EntityHuman ent, MatrixStack mStack, float ageInTicks, float rotationYaw, float partialTicks) {
        float f = ent.getSwimAnimation(partialTicks);
        if(ent.isElytraFlying()) {
            super.applyRotations(ent, mStack, ageInTicks, rotationYaw, partialTicks);
            float f1 = ent.getTicksElytraFlying() + partialTicks;
            float f2 = MathHelper.clamp(f1 * f1 / 100.0F, 0.0F, 1.0F);
            if(!ent.isSpinAttacking()) {
                mStack.rotate(Vector3f.XP.rotationDegrees(f2 * (-90.0F - ent.rotationPitch)));
            }

            Vec3d vec3d = ent.getLook(partialTicks);
            Vec3d vec3d1 = ent.getMotion();
            double d0 = Entity.horizontalMag(vec3d1);
            double d1 = Entity.horizontalMag(vec3d);
            if(d0 > 0.0D && d1 > 0.0D) {
                double d2 = (vec3d1.x * vec3d.x + vec3d1.z * vec3d.z) / (Math.sqrt(d0) * Math.sqrt(d1));
                double d3 = vec3d1.x * vec3d.z - vec3d1.z * vec3d.x;
                mStack.rotate(Vector3f.YP.rotation((float) (Math.signum(d3) * Math.acos(d2))));
            }
        } else if(f > 0.0F) {
            super.applyRotations(ent, mStack, ageInTicks, rotationYaw, partialTicks);
            float f3 = ent.isInWater() ? -90.0F - ent.rotationPitch : -90.0F;
            float f4 = MathHelper.lerp(f, 0.0F, f3);
            mStack.rotate(Vector3f.XP.rotationDegrees(f4));
            if(ent.isActualySwimming()) {
                mStack.translate(0.0D, -1.0D, (double) 0.3F);
            }
        } else {
            super.applyRotations(ent, mStack, ageInTicks, rotationYaw, partialTicks);
        }
    }
}
