package dev.crucifixbeta.api.utils.render;

import asm.accessors.ILivingEntity;
import asm.accessors.IModelPart;
import asm.accessors.IModelPartCuboid;
import asm.accessors.IPlayerEntityModel;
import com.mojang.blaze3d.systems.RenderSystem;
import dev.crucifixbeta.api.utils.Wrapper;
import dev.crucifixbeta.api.utils.math.MathUtil;
import dev.crucifixbeta.mod.modules.settings.impl.ColorSetting;
import java.awt.Color;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.model.ModelPart.Cuboid;
import net.minecraft.client.model.ModelPart.Quad;
import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.render.Tessellator;
import net.minecraft.client.render.VertexFormats;
import net.minecraft.client.render.VertexFormat.DrawMode;
import net.minecraft.client.render.entity.EntityRendererFactory.Context;
import net.minecraft.client.render.entity.model.EntityModelLayers;
import net.minecraft.client.render.entity.model.PlayerEntityModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RotationAxis;
import net.minecraft.util.math.Vec3d;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import org.joml.Vector4f;

@Environment(EnvType.CLIENT)
public class ModelPlayer extends PlayerEntityModel<PlayerEntity> {
   public static float LINE_WIDTH = 1.5F;
   public final PlayerEntity player;
   private static final Vector4f pos1 = new Vector4f();
   private static final Vector4f pos2 = new Vector4f();
   private static final Vector4f pos3 = new Vector4f();
   private static final Vector4f pos4 = new Vector4f();

   public ModelPlayer(PlayerEntity player) {
      super(
         new Context(
               Wrapper.mc.getEntityRenderDispatcher(),
               Wrapper.mc.getItemRenderer(),
               Wrapper.mc.getBlockRenderManager(),
               Wrapper.mc.getEntityRenderDispatcher().getHeldItemRenderer(),
               Wrapper.mc.getResourceManager(),
               Wrapper.mc.getEntityModelLoader(),
               Wrapper.mc.textRenderer
            )
            .getPart(EntityModelLayers.PLAYER),
         false
      );
      this.player = player;
      ((IPlayerEntityModel)this).getLeftPants().visible = false;
      ((IPlayerEntityModel)this).getRightPants().visible = false;
      ((IPlayerEntityModel)this).getLeftSleeve().visible = false;
      ((IPlayerEntityModel)this).getRightSleeve().visible = false;
      ((IPlayerEntityModel)this).getJacket().visible = false;
      this.hat.visible = false;
      this.getHead().scale(new Vector3f(-0.05F, -0.05F, -0.05F));
      this.sneaking = player.isInSneakingPose();
   }

   public void render(MatrixStack matrices, ColorSetting fill, ColorSetting line) {
      this.render(matrices, fill, line, 1.0, 0.0, 1.0, 0.0, false, false);
   }

   public void render(
      MatrixStack matrices, ColorSetting fill, ColorSetting line, double alpha, double yOffset, double scale, double yaw, boolean noLimb, boolean forceSneaking
   ) {
      if (forceSneaking) {
         this.sneaking = true;
      }

      double var14 = this.player.getX() - Wrapper.mc.getEntityRenderDispatcher().camera.getPos().getX();
      double var16 = this.player.getY() - Wrapper.mc.getEntityRenderDispatcher().camera.getPos().getY() + yOffset;
      double var18 = this.player.getZ() - Wrapper.mc.getEntityRenderDispatcher().camera.getPos().getZ();
      matrices.push();
      matrices.translate((float)var14, (float)var16, (float)var18);
      matrices.multiply(RotationAxis.POSITIVE_Y.rotation(MathUtil.rad(180.0F - this.player.bodyYaw + (float)yaw)));
      this.handSwingProgress = this.player.getHandSwingProgress(1.0F);
      float var20 = ((ILivingEntity)this.player).getLeaningPitch();
      if (this.player.isFallFlying()) {
         float var21 = this.player.getPitch();
         float var22 = (float)this.player.getFallFlyingTicks() + this.player.bodyYaw + (float)yaw;
         float var23 = MathHelper.clamp(var22 * var22 / 100.0F, 0.0F, 1.0F);
         if (!this.player.isUsingRiptide()) {
            matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(var23 * (-90.0F - var21)));
         }

         Vec3d var24 = this.player.getRotationVec(1.0F);
         Vec3d var25 = this.player.getVelocity();
         double var26 = var25.horizontalLengthSquared();
         double var28 = var24.horizontalLengthSquared();
         if (var26 > 0.0 && var28 > 0.0) {
            double var30 = (var25.x * var24.x + var25.z * var24.z) / Math.sqrt(var26 * var28);
            double var32 = var25.x * var24.z - var25.z * var24.x;
            matrices.multiply(RotationAxis.POSITIVE_Y.rotation((float)(Math.signum(var32) * Math.acos(var30))));
         }
      } else if (var20 > 0.0F) {
         float var34 = this.player.getPitch();
         float var35 = this.player.isTouchingWater() ? -90.0F - var34 : -90.0F;
         float var36 = MathHelper.lerp(var20, 0.0F, var35);
         matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(var36));
         if (this.player.isInSwimmingPose()) {
            matrices.translate(0.0F, -1.0F, 0.3F);
         }
      }

      matrices.scale(-1.0F, -1.0F, 1.0F);
      matrices.translate(0.0F, -1.401F, 0.0F);
      matrices.scale((float)scale * 0.93F, (float)scale * 0.93F, (float)scale * 0.93F);
      this.animateModel(
         this.player,
         noLimb ? 0.0F : this.player.limbAnimator.getPos(),
         noLimb ? 0.0F : this.player.limbAnimator.getSpeed(),
         Wrapper.mc.getRenderTickCounter().getTickDelta(true)
      );
      this.setAngles(
         this.player,
         noLimb ? 0.0F : this.player.limbAnimator.getPos(),
         noLimb ? 0.0F : this.player.limbAnimator.getSpeed(),
         (float)this.player.age,
         this.player.headYaw - this.player.bodyYaw,
         this.player.getPitch()
      );
      this.riding = this.player.hasVehicle();
      RenderSystem.enableBlend();
      RenderSystem.disableCull();
      RenderSystem.disableDepthTest();
      this.getHeadParts().forEach(modelPart -> render(matrices, modelPart, fill, line, alpha, false));
      this.getBodyParts().forEach(modelPart -> render(matrices, modelPart, fill, line, alpha, false));
      matrices.pop();
      RenderSystem.enableCull();
      RenderSystem.disableBlend();
      RenderSystem.enableDepthTest();
   }

   public static void render(MatrixStack matrices, ModelPart part, ColorSetting fill, ColorSetting line, double alpha, boolean texture) {
      if (part.visible && (!((IModelPart)(Object)part).getCuboids().isEmpty() || !((IModelPart)(Object)part).getChildren().isEmpty())) {
         matrices.push();
         part.rotate(matrices);

         for (Cuboid var8 : ((IModelPart)(Object)part).getCuboids()) {
            render(matrices, var8, fill, line, alpha, texture);
         }

         for (ModelPart var10 : ((IModelPart)(Object)part).getChildren().values()) {
            render(matrices, var10, fill, line, alpha, texture);
         }

         matrices.pop();
      }
   }

   public static void render(MatrixStack matrices, Cuboid cuboid, ColorSetting fill, ColorSetting line, double alpha, boolean texture) {
      Matrix4f var7 = matrices.peek().getPositionMatrix();

      for (Quad var11 : ((IModelPartCuboid)cuboid).getSides()) {
         pos1.set(var11.vertices[0].pos.x / 16.0F, var11.vertices[0].pos.y / 16.0F, var11.vertices[0].pos.z / 16.0F, 1.0F);
         pos1.mul(var7);
         pos2.set(var11.vertices[1].pos.x / 16.0F, var11.vertices[1].pos.y / 16.0F, var11.vertices[1].pos.z / 16.0F, 1.0F);
         pos2.mul(var7);
         pos3.set(var11.vertices[2].pos.x / 16.0F, var11.vertices[2].pos.y / 16.0F, var11.vertices[2].pos.z / 16.0F, 1.0F);
         pos3.mul(var7);
         pos4.set(var11.vertices[3].pos.x / 16.0F, var11.vertices[3].pos.y / 16.0F, var11.vertices[3].pos.z / 16.0F, 1.0F);
         pos4.mul(var7);
         if (fill.booleanValue) {
            Color var12 = fill.getValue();
            float var13 = (float)((double)((float)var12.getAlpha() / 255.0F) * alpha);
            float var14 = (float)var12.getRed() / 255.0F;
            float var15 = (float)var12.getGreen() / 255.0F;
            float var16 = (float)var12.getBlue() / 255.0F;
            RenderSystem.setShader(GameRenderer::getPositionColorProgram);
            BufferBuilder var17 = Tessellator.getInstance().begin(DrawMode.QUADS, VertexFormats.POSITION_COLOR);
            var17.vertex(pos1.x, pos1.y, pos1.z).color(var14, var15, var16, var13);
            var17.vertex(pos2.x, pos2.y, pos2.z).color(var14, var15, var16, var13);
            var17.vertex(pos3.x, pos3.y, pos3.z).color(var14, var15, var16, var13);
            var17.vertex(pos4.x, pos4.y, pos4.z).color(var14, var15, var16, var13);
            Render3DUtil.endBuilding(var17);
         }

         if (line.booleanValue) {
            Color var18 = line.getValue();
            float var19 = (float)((double)((float)var18.getAlpha() / 255.0F) * alpha);
            float var20 = (float)var18.getRed() / 255.0F;
            float var21 = (float)var18.getGreen() / 255.0F;
            float var22 = (float)var18.getBlue() / 255.0F;
            RenderSystem.setShader(GameRenderer::getPositionColorProgram);
            RenderSystem.lineWidth(Math.max(0.1F, LINE_WIDTH));
            BufferBuilder var23 = Tessellator.getInstance().begin(DrawMode.DEBUG_LINES, VertexFormats.POSITION_COLOR);
            var23.vertex(pos1.x, pos1.y, pos1.z).color(var20, var21, var22, var19);
            var23.vertex(pos2.x, pos2.y, pos2.z).color(var20, var21, var22, var19);
            var23.vertex(pos2.x, pos2.y, pos2.z).color(var20, var21, var22, var19);
            var23.vertex(pos3.x, pos3.y, pos3.z).color(var20, var21, var22, var19);
            var23.vertex(pos3.x, pos3.y, pos3.z).color(var20, var21, var22, var19);
            var23.vertex(pos4.x, pos4.y, pos4.z).color(var20, var21, var22, var19);
            var23.vertex(pos4.x, pos4.y, pos4.z).color(var20, var21, var22, var19);
            var23.vertex(pos1.x, pos1.y, pos1.z).color(var20, var21, var22, var19);
            Render3DUtil.endBuilding(var23);
            RenderSystem.lineWidth(1.0F);
         }
      }
   }
}
