package asm.mixins;

import com.mojang.blaze3d.systems.RenderSystem;
import dev.crucifixbeta.api.utils.Wrapper;
import dev.crucifixbeta.api.utils.math.MathUtil;
import dev.crucifixbeta.api.utils.render.ModelPlayer;
import dev.crucifixbeta.core.impl.RotationManager;
import dev.crucifixbeta.mod.modules.impl.client.ClientSetting;
import dev.crucifixbeta.mod.modules.impl.render.Chams;
import dev.crucifixbeta.mod.modules.impl.render.NoRender;
import java.awt.Color;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.entity.LivingEntityRenderer;
import net.minecraft.client.render.entity.model.CompositeEntityModel;
import net.minecraft.client.render.entity.model.EntityModel;
import net.minecraft.client.render.entity.model.SinglePartEntityModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArgs;
import org.spongepowered.asm.mixin.injection.At.Shift;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.invoke.arg.Args;

@Environment(EnvType.CLIENT)
@Mixin({LivingEntityRenderer.class})
public abstract class MixinLivingEntityRenderer<T extends LivingEntity, M extends EntityModel<T>> {
   @Shadow
   protected M model;
   @Unique
   private LivingEntity lastEntity;
   @Unique
   private float originalYaw;
   @Unique
   private float originalHeadYaw;
   @Unique
   private float originalBodyYaw;
   @Unique
   private float originalPitch;
   @Unique
   private float originalPrevYaw;
   @Unique
   private float originalPrevHeadYaw;
   @Unique
   private float originalPrevBodyYaw;

   @Inject(
      method = {"render*"},
      at = {@At("HEAD")}
   )
   public void onRenderPre(T livingEntity, float f, float g, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, CallbackInfo ci) {
      if (MinecraftClient.getInstance().player != null && livingEntity == MinecraftClient.getInstance().player && ClientSetting.INSTANCE.rotations.getValue()) {
         this.originalYaw = livingEntity.getYaw();
         this.originalHeadYaw = livingEntity.headYaw;
         this.originalBodyYaw = livingEntity.bodyYaw;
         this.originalPitch = livingEntity.getPitch();
         this.originalPrevYaw = livingEntity.prevYaw;
         this.originalPrevHeadYaw = livingEntity.prevHeadYaw;
         this.originalPrevBodyYaw = livingEntity.prevBodyYaw;
         livingEntity.setYaw(RotationManager.getRenderYawOffset());
         livingEntity.headYaw = RotationManager.getRotationYawHead();
         livingEntity.bodyYaw = RotationManager.getRenderYawOffset();
         livingEntity.setPitch(RotationManager.getRenderPitch());
         livingEntity.prevYaw = RotationManager.getPrevRenderYawOffset();
         livingEntity.prevHeadYaw = RotationManager.getPrevRotationYawHead();
         livingEntity.prevBodyYaw = RotationManager.getPrevRenderYawOffset();
         livingEntity.prevPitch = RotationManager.getPrevRenderPitch();
      }

      this.lastEntity = livingEntity;
   }

   @Inject(
      method = {"render*"},
      at = {@At("TAIL")}
   )
   public void onRenderPost(T livingEntity, float f, float g, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, CallbackInfo ci) {
      if (MinecraftClient.getInstance().player != null && livingEntity == MinecraftClient.getInstance().player && ClientSetting.INSTANCE.rotations.getValue()) {
         livingEntity.setYaw(this.originalYaw);
         livingEntity.headYaw = this.originalHeadYaw;
         livingEntity.bodyYaw = this.originalBodyYaw;
         livingEntity.setPitch(this.originalPitch);
         livingEntity.prevYaw = this.originalPrevYaw;
         livingEntity.prevHeadYaw = this.originalPrevHeadYaw;
         livingEntity.prevBodyYaw = this.originalPrevBodyYaw;
         livingEntity.prevPitch = this.originalPitch;
      }
   }

   @Inject(
      method = {"render*"},
      at = {@At(
         value = "INVOKE",
         target = "Lnet/minecraft/client/render/entity/model/EntityModel;render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;III)V",
         shift = Shift.AFTER
      )}
   )
   private void renderChamsModel(
      T livingEntity, float f, float g, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, CallbackInfo ci
   ) {
      Chams var8 = Chams.INSTANCE;
      if (var8 != null && var8.shouldRenderModel(livingEntity)) {
         RenderSystem.enableBlend();
         if (var8.shouldRenderHiddenLayer()) {
            RenderSystem.disableDepthTest();
            double var9 = var8.getEntityAlpha();
            this.renderModelParts(matrixStack, this.model, var8, var9, true);
         }

         if (var8.shouldUseEntityDepth()) {
            RenderSystem.enableDepthTest();
         } else {
            RenderSystem.disableDepthTest();
         }

         double var11 = var8.getEntityAlpha();
         this.renderModelParts(matrixStack, this.model, var8, var11, false);
         RenderSystem.enableDepthTest();
         RenderSystem.disableBlend();
      }
   }

   @Unique
   private void renderModelParts(MatrixStack matrixStack, EntityModel<T> model, Chams module, double alpha, boolean hidden) {
      if (model instanceof SinglePartEntityModel var11) {
         ModelPlayer.render(matrixStack, var11.getPart(), module.getModelFill(hidden), module.getModelLine(hidden), alpha, false);
      } else {
         if (model instanceof CompositeEntityModel var7) {
            for (Object var9 : var7.getParts()) {
               if (var9 instanceof ModelPart var10) {
                  ModelPlayer.render(matrixStack, var10, module.getModelFill(hidden), module.getModelLine(hidden), alpha, false);
               }
            }
         }
      }
   }

   @ModifyArgs(
      method = {"render*"},
      at = @At(
         value = "INVOKE",
         target = "Lnet/minecraft/client/render/entity/model/EntityModel;render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;III)V"
      )
   )
   private void renderHook(Args args) {
      float var2 = -1.0F;
      if (NoRender.INSTANCE.isOn() && NoRender.INSTANCE.antiPlayerCollision.getValue() && this.lastEntity != Wrapper.mc.player) {
         LivingEntity var3 = this.lastEntity;
         PlayerEntity var4;
         if (this.lastEntity instanceof PlayerEntity && !(var4 = (PlayerEntity)var3).isInvisible()) {
            var2 = MathUtil.clamp((float)(Wrapper.mc.player.squaredDistanceTo(this.lastEntity.getPos()) / 3.0) + 0.2F, 0.0F, 1.0F);
         }
      }

      Chams var5 = Chams.INSTANCE;
      if (var5 != null && var5.shouldHideOriginalTexture(this.lastEntity)) {
         args.set(4, 16777215);
      }

      if (var2 != -1.0F) {
         args.set(4, this.applyOpacity(654311423, var2));
      }
   }

   @Unique
   int applyOpacity(int color_int, float opacity) {
      opacity = Math.min(1.0F, Math.max(0.0F, opacity));
      Color var3 = new Color(color_int);
      return new Color(var3.getRed(), var3.getGreen(), var3.getBlue(), (int)((float)var3.getAlpha() * opacity)).getRGB();
   }
}
