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

import dev.crucifixbeta.CrucifixBeta;
import dev.crucifixbeta.api.utils.math.Timer;
import dev.crucifixbeta.api.utils.render.ColorUtil;
import dev.crucifixbeta.api.utils.render.Render3DUtil;
import dev.crucifixbeta.api.utils.world.BlockUtil;
import dev.crucifixbeta.mod.modules.Module;
import dev.crucifixbeta.mod.modules.settings.impl.BooleanSetting;
import dev.crucifixbeta.mod.modules.settings.impl.ColorSetting;
import dev.crucifixbeta.mod.modules.settings.impl.SliderSetting;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.Blocks;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Direction;

@Environment(EnvType.CLIENT)
public class HoleESP extends Module {
   public static HoleESP INSTANCE;
   public final SliderSetting startFade = this.add(new SliderSetting("HoleStartFade", 5.0, 1.0, 20.0, this::holeSettingsVisible));
   public final SliderSetting getDistance = this.add(new SliderSetting("HoleDistance", 6.0, 1.0, 20.0, this::holeSettingsVisible));
   public final SliderSetting airHeight = this.add(new SliderSetting("HoleAirHeight", 1.0, -3.0, 3.0, 0.01, this::holeSettingsVisible));
   public final BooleanSetting airYCheck = this.add(new BooleanSetting("HoleAirYCheck", true, this::holeSettingsVisible));
   public final SliderSetting height = this.add(new SliderSetting("HoleHeight", 1.0, -3.0, 3.0, 0.1, this::holeSettingsVisible));
   public final SliderSetting wallHeight = this.add(new SliderSetting("HoleWallHeight", 3.0, -3.0, 3.0, 0.1, this::holeSettingsVisible));
   public final BooleanSetting sideCheck = this.add(new BooleanSetting("HoleSideCheck", true, this::holeSettingsVisible));
   private final ColorSetting airFill = this.add(
      new ColorSetting("HoleAirFill", new Color(148, 0, 0, 100), this::holeSettingsVisible).injectBoolean(true)
   );
   private final ColorSetting airBox = this.add(
      new ColorSetting("HoleAirBox", new Color(148, 0, 0, 100), this::holeSettingsVisible).injectBoolean(true)
   );
   private final ColorSetting airFade = this.add(
      new ColorSetting("HoleAirFade", new Color(148, 0, 0, 0), this::holeSettingsVisible).injectBoolean(true)
   );
   private final ColorSetting normalFill = this.add(
      new ColorSetting("HoleUnsafeFill", new Color(255, 0, 0, 50), this::holeSettingsVisible).injectBoolean(true)
   );
   private final ColorSetting normalBox = this.add(
      new ColorSetting("HoleUnsafeBox", new Color(255, 0, 0, 100), this::holeSettingsVisible).injectBoolean(true)
   );
   private final ColorSetting normalFade = this.add(
      new ColorSetting("HoleUnsafeFade", new Color(255, 0, 0, 0), this::holeSettingsVisible).injectBoolean(true)
   );
   private final ColorSetting bedrockFill = this.add(
      new ColorSetting("HoleSafeFill", new Color(8, 255, 79, 50), this::holeSettingsVisible).injectBoolean(true)
   );
   private final ColorSetting bedrockBox = this.add(
      new ColorSetting("HoleSafeBox", new Color(8, 255, 79, 100), this::holeSettingsVisible).injectBoolean(true)
   );
   private final ColorSetting bedrockFade = this.add(
      new ColorSetting("HoleSafeFade", new Color(8, 255, 79, 100), this::holeSettingsVisible).injectBoolean(true)
   );
   private final ColorSetting wallFill = this.add(
      new ColorSetting("HoleWallFill", new Color(0, 255, 255, 128), this::holeSettingsVisible).injectBoolean(true)
   );
   private final ColorSetting wallBox = this.add(
      new ColorSetting("HoleWallBox", new Color(0, 225, 255, 255), this::holeSettingsVisible).injectBoolean(true)
   );
   private final ColorSetting wallFade = this.add(
      new ColorSetting("HoleWallFade", new Color(0, 255, 255, 64), this::holeSettingsVisible).injectBoolean(true)
   );
   private final ColorSetting wallSideFill = this.add(
      new ColorSetting("HoleWallSideFill", new Color(0, 255, 255, 128), this::holeSettingsVisible).injectBoolean(true)
   );
   private final ColorSetting wallSideBox = this.add(
      new ColorSetting("HoleWallSideBox", new Color(0, 225, 255, 255), this::holeSettingsVisible).injectBoolean(true)
   );
   private final ColorSetting wallSideFade = this.add(
      new ColorSetting("HoleWallSideFade", new Color(0, 255, 255, 64), this::holeSettingsVisible).injectBoolean(true)
   );
   private final SliderSetting updateDelay = this.add(new SliderSetting("HoleUpdateDelay", 50, 0, 1000, this::holeSettingsVisible));
   private final List<BlockPos> tempNormalList = new ArrayList<>();
   private final List<BlockPos> tempBedrockList = new ArrayList<>();
   private final List<BlockPos> tempAirList = new ArrayList<>();
   private final List<BlockPos> tempWallList = new ArrayList<>();
   private final List<BlockPos> tempWallSideList = new ArrayList<>();
   private final Timer timer = new Timer();
   boolean drawing = false;
   private List<BlockPos> normalList = new ArrayList<>();
   private List<BlockPos> bedrockList = new ArrayList<>();
   private List<BlockPos> airList = new ArrayList<>();
   private List<BlockPos> wallList = new ArrayList<>();
   private List<BlockPos> wallSideList = new ArrayList<>();

   public HoleESP() {
      this("HoleESP", Module.Category.Render);
      this.setChinese("\u5751\u900f\u89c6");
      INSTANCE = this;
   }

   protected HoleESP(String name, Module.Category category) {
      super(name, category);
      INSTANCE = this;
   }

   public void onThread() {
      if (!nullCheck() && !this.isOff()) {
         if (!this.drawing && this.timer.passedMs(this.updateDelay.getValue())) {
            this.normalList = new ArrayList<>(this.tempNormalList);
            this.bedrockList = new ArrayList<>(this.tempBedrockList);
            this.airList = new ArrayList<>(this.tempAirList);
            this.wallList = new ArrayList<>(this.tempWallList);
            this.wallSideList = new ArrayList<>(this.tempWallSideList);
            this.timer.reset();
            this.tempBedrockList.clear();
            this.tempNormalList.clear();
            this.tempAirList.clear();
            this.tempWallList.clear();
            this.tempWallSideList.clear();

            for (BlockPos var2 : BlockUtil.getSphere(this.getDistance.getValueFloat(), mc.player.getPos())) {
               if (this.isBedrock(var2) && this.isBedrock(var2.up(2)) && this.isBedrock(var2.down())) {
                  Direction var3 = this.getWallSide(var2);
                  if (var3 != null || !this.sideCheck.getValue()) {
                     this.tempWallList.add(var2);
                  }

                  if (var3 != null) {
                     this.tempWallSideList.add(var2.offset(var3));
                  }
               }

               HoleESP.Type var4;
               if ((var4 = this.isHole(var2)) == HoleESP.Type.Bedrock) {
                  this.tempBedrockList.add(var2);
               } else if (var4 == HoleESP.Type.Normal) {
                  this.tempNormalList.add(var2);
               } else if (var4 == HoleESP.Type.Air) {
                  this.tempAirList.add(var2);
               }
            }
         }
      }
   }

   private Direction getWallSide(BlockPos pos) {
      double var2 = Double.MAX_VALUE;
      Direction var4 = null;

      for (Direction var8 : Direction.values()) {
         BlockPos var9;
         if (var8 != Direction.UP
            && var8 != Direction.DOWN
            && BlockUtil.canCollide(new Box((var9 = pos.offset(var8)).down()))
            && !BlockUtil.canCollide(new Box(var9))
            && !BlockUtil.canCollide(new Box(var9.up()))) {
            if (var4 == null) {
               var4 = var8;
               var2 = mc.player.getEyePos().distanceTo(var9.toCenterPos());
            } else if (mc.player.getEyePos().distanceTo(var9.toCenterPos()) < var2) {
               var4 = var8;
               var2 = mc.player.getEyePos().distanceTo(var9.toCenterPos());
            }
         }
      }

      return var4;
   }

   private boolean isBedrock(BlockPos pos) {
      return mc.world.getBlockState(pos).getBlock() == Blocks.BEDROCK;
   }

   HoleESP.Type isHole(BlockPos pos) {
      if (mc.world.isAir(pos)
         && (!this.airYCheck.getValue() || pos.getY() == mc.player.getBlockY() - 1 || pos.getY() == mc.player.getBlockY())
         && CrucifixBeta.HOLE.isHard(pos.up())) {
         return HoleESP.Type.Air;
      } else {
         int var2 = 0;
         boolean var3 = true;

         for (Direction var7 : Direction.values()) {
            if (var7 != Direction.UP && var7 != Direction.DOWN && CrucifixBeta.HOLE.isHard(pos.offset(var7))) {
               if (mc.world.getBlockState(pos.offset(var7)).getBlock() != Blocks.BEDROCK) {
                  var3 = false;
               }

               var2++;
            }
         }

         if (mc.world.isAir(pos) && mc.world.isAir(pos.up()) && mc.world.isAir(pos.up(2)) && var2 > 3 && BlockUtil.canCollide(mc.player, new Box(pos.down()))) {
            return var3 ? HoleESP.Type.Bedrock : HoleESP.Type.Normal;
         } else {
            return CrucifixBeta.HOLE.isDoubleHole(pos) ? HoleESP.Type.Normal : HoleESP.Type.None;
         }
      }
   }

   @Override
   public void onRender3D(MatrixStack matrixStack) {
      this.drawing = true;
      this.draw(matrixStack, this.bedrockList, this.bedrockFill, this.bedrockFade, this.bedrockBox, this.height.getValue());
      this.draw(matrixStack, this.airList, this.airFill, this.airFade, this.airBox, this.airHeight.getValue());
      this.draw(matrixStack, this.normalList, this.normalFill, this.normalFade, this.normalBox, this.height.getValue());
      this.draw(matrixStack, this.wallList, this.wallFill, this.wallFade, this.wallBox, this.wallHeight.getValue());
      this.draw(matrixStack, this.wallSideList, this.wallSideFill, this.wallSideFade, this.wallSideBox, this.height.getValue());
      this.drawing = false;
   }

   private void draw(MatrixStack matrixStack, List<BlockPos> list, ColorSetting fill, ColorSetting fade, ColorSetting box, double height) {
      for (BlockPos var9 : list) {
         double var10 = mc.player.getPos().distanceTo(var9.toCenterPos());
         double var12 = var10 > this.startFade.getValue()
            ? Math.max(Math.min(1.0, 1.0 - (var10 - this.startFade.getValue()) / (this.getDistance.getValue() - this.startFade.getValue())), 0.0)
            : 1.0;
         Box var14 = new Box(
            (double)var9.getX(), (double)var9.getY(), (double)var9.getZ(), (double)(var9.getX() + 1), (double)var9.getY() + height, (double)(var9.getZ() + 1)
         );
         if (fill.booleanValue) {
            if (fade.booleanValue) {
               Render3DUtil.drawFadeFill(
                  matrixStack,
                  var14,
                  ColorUtil.injectAlpha(fill.getValue(), (int)((double)fill.getValue().getAlpha() * var12)),
                  ColorUtil.injectAlpha(fade.getValue(), (int)((double)fade.getValue().getAlpha() * var12))
               );
            } else {
               Render3DUtil.drawFill(matrixStack, var14, ColorUtil.injectAlpha(fill.getValue(), (int)((double)fill.getValue().getAlpha() * var12)));
            }
         }

         if (box.booleanValue) {
            Render3DUtil.drawBox(matrixStack, var14, ColorUtil.injectAlpha(box.getValue(), (int)((double)box.getValue().getAlpha() * var12)));
         }
      }
   }

   protected boolean holeSettingsVisible() {
      return true;
   }
@Environment(EnvType.CLIENT)
   public static enum Type {
      None,
      Air,
      Normal,
      Bedrock;

}
}
