package dev.crucifixbeta.mod.modules.impl.render;

import asm.accessors.IHeldItemRenderer;
import dev.crucifixbeta.api.events.eventbus.EventListener;
import dev.crucifixbeta.api.events.impl.HeldItemRendererEvent;
import dev.crucifixbeta.mod.modules.Module;
import dev.crucifixbeta.mod.modules.settings.impl.BooleanSetting;
import dev.crucifixbeta.mod.modules.settings.impl.EnumSetting;
import dev.crucifixbeta.mod.modules.settings.impl.SliderSetting;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.item.ToolItem;
import net.minecraft.util.Hand;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RotationAxis;

@Environment(EnvType.CLIENT)
public class ViewModel extends SwingModule {
   public static ViewModel INSTANCE;
   private final EnumSetting<ViewModel.Page> page = this.add(new EnumSetting<>("Page", ViewModel.Page.Model));
   public final BooleanSetting mainhandSwap = this.add(new BooleanSetting("MainhandSwap", true, () -> this.page.is(ViewModel.Page.Model)));
   public final BooleanSetting offhandSwap = this.add(new BooleanSetting("OffhandSwap", true, () -> this.page.is(ViewModel.Page.Model)));
   public final SliderSetting scaleMainX = this.add(
      new SliderSetting("ScaleMainX", 1.0, 0.1F, 5.0, 0.01, () -> this.page.is(ViewModel.Page.Model))
   );
   public final SliderSetting scaleMainY = this.add(
      new SliderSetting("ScaleMainY", 1.0, 0.1F, 5.0, 0.01, () -> this.page.is(ViewModel.Page.Model))
   );
   public final SliderSetting scaleMainZ = this.add(
      new SliderSetting("ScaleMainZ", 1.0, 0.1F, 5.0, 0.01, () -> this.page.is(ViewModel.Page.Model))
   );
   public final SliderSetting positionMainX = this.add(
      new SliderSetting("PositionMainX", 0.0, -3.0, 3.0, 0.01, () -> this.page.is(ViewModel.Page.Model))
   );
   public final SliderSetting positionMainY = this.add(
      new SliderSetting("PositionMainY", 0.0, -3.0, 3.0, 0.01, () -> this.page.is(ViewModel.Page.Model))
   );
   public final SliderSetting positionMainZ = this.add(
      new SliderSetting("PositionMainZ", 0.0, -3.0, 3.0, 0.01, () -> this.page.is(ViewModel.Page.Model))
   );
   public final SliderSetting rotationMainX = this.add(
      new SliderSetting("RotationMainX", 0.0, -180.0, 180.0, 0.01, () -> this.page.is(ViewModel.Page.Model))
   );
   public final SliderSetting rotationMainY = this.add(
      new SliderSetting("RotationMainY", 0.0, -180.0, 180.0, 0.01, () -> this.page.is(ViewModel.Page.Model))
   );
   public final SliderSetting rotationMainZ = this.add(
      new SliderSetting("RotationMainZ", 0.0, -180.0, 180.0, 0.01, () -> this.page.is(ViewModel.Page.Model))
   );
   public final SliderSetting scaleOffX = this.add(
      new SliderSetting("ScaleOffX", 1.0, 0.1F, 5.0, 0.01, () -> this.page.is(ViewModel.Page.Model))
   );
   public final SliderSetting scaleOffY = this.add(
      new SliderSetting("ScaleOffY", 1.0, 0.1F, 5.0, 0.01, () -> this.page.is(ViewModel.Page.Model))
   );
   public final SliderSetting scaleOffZ = this.add(
      new SliderSetting("ScaleOffZ", 1.0, 0.1F, 5.0, 0.01, () -> this.page.is(ViewModel.Page.Model))
   );
   public final SliderSetting positionOffX = this.add(
      new SliderSetting("PositionOffX", 0.0, -3.0, 3.0, 0.01, () -> this.page.is(ViewModel.Page.Model))
   );
   public final SliderSetting positionOffY = this.add(
      new SliderSetting("PositionOffY", 0.0, -3.0, 3.0, 0.01, () -> this.page.is(ViewModel.Page.Model))
   );
   public final SliderSetting positionOffZ = this.add(
      new SliderSetting("PositionOffZ", 0.0, -3.0, 3.0, 0.01, () -> this.page.is(ViewModel.Page.Model))
   );
   public final SliderSetting rotationOffX = this.add(
      new SliderSetting("RotationOffX", 0.0, -180.0, 180.0, 0.01, () -> this.page.is(ViewModel.Page.Model))
   );
   public final SliderSetting rotationOffY = this.add(
      new SliderSetting("RotationOffY", 0.0, -180.0, 180.0, 0.01, () -> this.page.is(ViewModel.Page.Model))
   );
   public final SliderSetting rotationOffZ = this.add(
      new SliderSetting("RotationOffZ", 0.0, -180.0, 180.0, 0.01, () -> this.page.is(ViewModel.Page.Model))
   );
   public final BooleanSetting slowAnimation = this.add(new BooleanSetting("OldSwingSpeed", true, () -> this.page.is(ViewModel.Page.Swing)));
   public final SliderSetting slowAnimationVal = this.add(new SliderSetting("OldValue", 6, 1, 50, () -> this.page.is(ViewModel.Page.Swing)));
   private final SliderSetting itemRotationSpeed = this.add(
      new SliderSetting("RotationSpeed", 4.0, 0.1F, 20.0, 0.1F, () -> this.page.is(ViewModel.Page.ItemRotation))
   );
   private final SliderSetting itemXRotation = this.add(
      new SliderSetting("ItemX-Rotation", 0.0, -180.0, 180.0, 1.0, () -> this.page.is(ViewModel.Page.ItemRotation))
   );
   private final SliderSetting itemYRotation = this.add(
      new SliderSetting("ItemY-Rotation", 0.0, -180.0, 180.0, 1.0, () -> this.page.is(ViewModel.Page.ItemRotation))
   );
   private final SliderSetting itemZRotation = this.add(
      new SliderSetting("ItemZ-Rotation", 0.0, -180.0, 180.0, 1.0, () -> this.page.is(ViewModel.Page.ItemRotation))
   );
   private final BooleanSetting itemToolsOnly = this.add(
      new BooleanSetting("ToolsOnly", false, () -> this.page.is(ViewModel.Page.ItemRotation))
   );
   private final BooleanSetting itemNoRotateWhenHurt = this.add(
      new BooleanSetting("NoHurtRotate", true, () -> this.page.is(ViewModel.Page.ItemRotation))
   );
   private float itemRotationAngle = 0.0F;
   private int lastSelectedSlot = -1;

   public ViewModel() {
      super("ViewModel", Module.Category.Render);
      this.setChinese("\u624b\u6301\u6a21\u578b");
      INSTANCE = this;
      SwingModule.INSTANCE = this;
   }

   @Override
   public void onRender3D(MatrixStack matrixStack) {
      if (!this.mainhandSwap.getValue() && ((IHeldItemRenderer)mc.getEntityRenderDispatcher().getHeldItemRenderer()).getEquippedProgressMainHand() <= 1.0F) {
         ((IHeldItemRenderer)mc.getEntityRenderDispatcher().getHeldItemRenderer()).setEquippedProgressMainHand(1.0F);
         ((IHeldItemRenderer)mc.getEntityRenderDispatcher().getHeldItemRenderer()).setItemStackMainHand(mc.player.getMainHandStack());
      }

      if (!this.offhandSwap.getValue() && ((IHeldItemRenderer)mc.getEntityRenderDispatcher().getHeldItemRenderer()).getEquippedProgressOffHand() <= 1.0F) {
         ((IHeldItemRenderer)mc.getEntityRenderDispatcher().getHeldItemRenderer()).setEquippedProgressOffHand(1.0F);
         ((IHeldItemRenderer)mc.getEntityRenderDispatcher().getHeldItemRenderer()).setItemStackOffHand(mc.player.getOffHandStack());
      }
   }

   @EventListener
   private void onHeldItemRender(HeldItemRendererEvent event) {
      if (this.page.is(ViewModel.Page.ItemRotation)) {
         this.applyItemRotation(event);
      }

      if (event.getHand() == Hand.MAIN_HAND) {
         event.getStack().translate(this.positionMainX.getValueFloat(), this.positionMainY.getValueFloat(), this.positionMainZ.getValueFloat());
         event.getStack().scale(this.scaleMainX.getValueFloat(), this.scaleMainY.getValueFloat(), this.scaleMainZ.getValueFloat());
         event.getStack().multiply(RotationAxis.POSITIVE_X.rotationDegrees(this.rotationMainX.getValueFloat()));
         event.getStack().multiply(RotationAxis.POSITIVE_Y.rotationDegrees(this.rotationMainY.getValueFloat()));
         event.getStack().multiply(RotationAxis.POSITIVE_Z.rotationDegrees(this.rotationMainZ.getValueFloat()));
      } else {
         event.getStack().translate(this.positionOffX.getValueFloat(), this.positionOffY.getValueFloat(), this.positionOffZ.getValueFloat());
         event.getStack().scale(this.scaleOffX.getValueFloat(), this.scaleOffY.getValueFloat(), this.scaleOffZ.getValueFloat());
         event.getStack().multiply(RotationAxis.POSITIVE_X.rotationDegrees(this.rotationOffX.getValueFloat()));
         event.getStack().multiply(RotationAxis.POSITIVE_Y.rotationDegrees(this.rotationOffY.getValueFloat()));
         event.getStack().multiply(RotationAxis.POSITIVE_Z.rotationDegrees(this.rotationOffZ.getValueFloat()));
      }
   }

   private void applyItemRotation(HeldItemRendererEvent event) {
      if (event.getHand() == Hand.MAIN_HAND) {
         if (!(mc.player.getAttackCooldownProgress(0.5F) < 1.0F)) {
            if (!this.itemNoRotateWhenHurt.getValue() || mc.player.hurtTime <= 0) {
               if (!this.itemToolsOnly.getValue() || mc.player.getMainHandStack().getItem() instanceof ToolItem) {
                  if (mc.player.getInventory().selectedSlot != this.lastSelectedSlot) {
                     this.itemRotationAngle = 0.0F;
                     this.lastSelectedSlot = mc.player.getInventory().selectedSlot;
                  }

                  this.itemRotationAngle = (this.itemRotationAngle + this.itemRotationSpeed.getValueFloat()) % 360.0F;
                  event.getStack().multiply(RotationAxis.POSITIVE_X.rotationDegrees(this.itemXRotation.getValueFloat()));
                  event.getStack()
                     .multiply(RotationAxis.POSITIVE_Y.rotationDegrees(MathHelper.wrapDegrees(this.itemYRotation.getValueFloat() + this.itemRotationAngle)));
                  event.getStack().multiply(RotationAxis.POSITIVE_Z.rotationDegrees(this.itemZRotation.getValueFloat()));
               }
            }
         }
      }
   }

   @Override
   protected boolean swingSettingsVisible() {
      return this.page.is(ViewModel.Page.Swing);
   }
@Environment(EnvType.CLIENT)
   private static enum Page {
      Model,
      Swing,
      ItemRotation;

}
}
