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

import dev.crucifixbeta.CrucifixBeta;
import dev.crucifixbeta.api.events.eventbus.EventListener;
import dev.crucifixbeta.api.events.impl.UpdateEvent;
import dev.crucifixbeta.api.utils.combat.CombatUtil;
import dev.crucifixbeta.api.utils.math.Timer;
import dev.crucifixbeta.api.utils.player.EntityUtil;
import dev.crucifixbeta.api.utils.player.InventoryUtil;
import dev.crucifixbeta.api.utils.world.BlockPosX;
import dev.crucifixbeta.api.utils.world.BlockUtil;
import dev.crucifixbeta.mod.modules.Module;
import dev.crucifixbeta.mod.modules.impl.client.AntiCheat;
import dev.crucifixbeta.mod.modules.impl.exploit.Blink;
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 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.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Direction;

@Environment(EnvType.CLIENT)
public class Blocker extends Module {
   public static Blocker INSTANCE;
   private final EnumSetting<Blocker.Page> page = this.add(new EnumSetting<>("Page", Blocker.Page.General));
   final List<BlockPos> placePos = new ArrayList<>();
   final List<BlockPos> blockerPos = new ArrayList<>();
   final List<BlockPos> list = new ArrayList<>();
   private final Timer timer = new Timer();
   private final SliderSetting delay = this.add(new SliderSetting("PlaceDelay", 50, 0, 500, () -> this.page.getValue() == Blocker.Page.General));
   private final SliderSetting blocksPer = this.add(new SliderSetting("BlocksPer", 1, 1, 8, () -> this.page.getValue() == Blocker.Page.General));
   private final BooleanSetting rotate = this.add(new BooleanSetting("Rotate", true, () -> this.page.getValue() == Blocker.Page.General));
   private final BooleanSetting breakCrystal = this.add(
      new BooleanSetting("Break", true, () -> this.page.getValue() == Blocker.Page.General)
   );
   private final BooleanSetting inventorySwap = this.add(
      new BooleanSetting("InventorySwap", true, () -> this.page.getValue() == Blocker.Page.General)
   );
   private final BooleanSetting bevelCev = this.add(new BooleanSetting("BevelCev", true, () -> this.page.getValue() == Blocker.Page.Target));
   private final BooleanSetting burrow = this.add(new BooleanSetting("Burrow", true, () -> this.page.getValue() == Blocker.Page.Target));
   private final BooleanSetting face = this.add(
      new BooleanSetting("Face", true, () -> this.page.getValue() == Blocker.Page.Target).setParent()
   );
   private final BooleanSetting faceUp = this.add(
      new BooleanSetting("FaceUp", false, () -> this.page.getValue() == Blocker.Page.Target && this.face.isOpen())
   );
   private final BooleanSetting feet = this.add(
      new BooleanSetting("Feet", true, () -> this.page.getValue() == Blocker.Page.Target).setParent()
   );
   private final BooleanSetting extend = this.add(
      new BooleanSetting("Extend", false, () -> this.page.getValue() == Blocker.Page.Target && this.feet.isOpen())
   );
   private final BooleanSetting onlySurround = this.add(
      new BooleanSetting("OnlySurround", true, () -> this.page.getValue() == Blocker.Page.Target && this.feet.isOpen())
   );
   private final BooleanSetting inAirPause = this.add(new BooleanSetting("InAirPause", false, () -> this.page.getValue() == Blocker.Page.Check));
   private final BooleanSetting detectMining = this.add(
      new BooleanSetting("DetectMining", true, () -> this.page.getValue() == Blocker.Page.Check)
   );
   private final BooleanSetting eatingPause = this.add(new BooleanSetting("EatingPause", true, () -> this.page.getValue() == Blocker.Page.Check));
   private int placeProgress = 0;
   private BlockPos playerBP;

   public Blocker() {
      super("Blocker", Module.Category.Combat);
      this.setChinese("\u6c34\u6676\u963b\u6321");
      INSTANCE = this;
   }

   @EventListener
   public void onUpdate(UpdateEvent event) {
      this.list.clear();
      if (!this.inventorySwap.getValue() || EntityUtil.inInventory()) {
         if (!Blink.INSTANCE.isOn() || !Blink.INSTANCE.pauseModule.getValue()) {
            if (this.timer.passedMs(this.delay.getValue())) {
               if (!this.eatingPause.getValue() || !this.shouldPauseForUsingItem()) {
                  this.placeProgress = 0;
                  if (this.playerBP != null && !this.playerBP.equals(EntityUtil.getPlayerPos(true))) {
                     this.placePos.clear();
                     this.blockerPos.clear();
                  }

                  this.playerBP = EntityUtil.getPlayerPos(true);
                  double[] var2 = new double[]{AntiCheat.getOffset(), -AntiCheat.getOffset(), 0.0};
                  if (this.bevelCev.getValue()) {
                     for (Direction var6 : Direction.values()) {
                        BlockPos var7;
                        if (var6 != Direction.DOWN
                           && !this.isBedrock(this.playerBP.offset(var6).up())
                           && this.crystalHere(var7 = this.playerBP.offset(var6).up(2))
                           && !this.placePos.contains(var7)) {
                           this.placePos.add(var7);
                        }
                     }
                  }

                  if (this.face.getValue() && (!this.onlySurround.getValue() || Surround.INSTANCE.isOn() || SelfTrap.INSTANCE.isOn())) {
                     for (double var8 : var2) {
                        for (double var13 : var2) {
                           for (Direction var18 : Direction.values()) {
                              for (int var19 = 0; var19 < 3; var19++) {
                                 BlockPos var20 = new BlockPosX(mc.player.getX() + var8, mc.player.getY() + 0.5, mc.player.getZ() + var13)
                                    .offset(var18, 1)
                                    .up();
                                 BlockPos var21 = new BlockPosX(mc.player.getX() + var8, mc.player.getY() + 0.5, mc.player.getZ() + var13)
                                    .offset(var18, var19)
                                    .up();
                                 if (this.crystalHere(var21) && !this.placePos.contains(var21) && !CrucifixBeta.HOLE.isHard(var20)) {
                                    this.placePos.add(var21);
                                 }
                              }
                           }
                        }
                     }

                     if (this.faceUp.getValue()) {
                        for (Direction var43 : Direction.values()) {
                           BlockPos var45;
                           if (var43 != Direction.DOWN
                              && !this.isBedrock(this.playerBP.offset(var43).up())
                              && this.crystalHere(var45 = this.playerBP.offset(var43).up(2))
                              && !this.placePos.contains(var45)) {
                              this.placePos.add(var45);
                           }
                        }
                     }
                  }

                  if (this.getObsidian() != -1) {
                     this.placePos.removeIf(pos -> !BlockUtil.clientCanPlace(pos, true));
                     if (this.burrow.getValue()) {
                        for (double var46 : var2) {
                           for (double var58 : var2) {
                              BlockPosX var61 = new BlockPosX(mc.player.getX() + var46, mc.player.getY(), mc.player.getZ() + var58);
                              if (!this.isBedrock(var61) && CrucifixBeta.BREAK.isMining(var61)) {
                                 for (Direction var73 : Direction.values()) {
                                    if (var73 != Direction.DOWN && var73 != Direction.UP) {
                                       BlockPos var76 = var61.offset(var73);
                                       if (!this.detectMining.getValue() || !CrucifixBeta.BREAK.isMining(var76)) {
                                          if (this.breakCrystal.getValue()) {
                                             CombatUtil.attackCrystal(var76, this.rotate.getValue(), false);
                                          }

                                          if (BlockUtil.canPlace(var76, 6.0, this.breakCrystal.getValue())) {
                                             this.blockerPos.add(var76);
                                          }
                                       }
                                    }
                                 }
                              }
                           }
                        }
                     }

                     if (this.feet.getValue() && (!this.onlySurround.getValue() || Surround.INSTANCE.isOn() || SelfTrap.INSTANCE.isOn())) {
                        for (double var47 : var2) {
                           for (double var59 : var2) {
                              for (Direction var71 : Direction.values()) {
                                 BlockPos var74 = new BlockPosX(mc.player.getX() + var47, mc.player.getY() + 0.5, mc.player.getZ() + var59).offset(var71);
                                 if (!this.isBedrock(var74) && CrucifixBeta.BREAK.isMining(var74)) {
                                    for (Direction var23 : Direction.values()) {
                                       BlockPos var24 = var74.offset(var23);
                                       if (!this.detectMining.getValue() || !CrucifixBeta.BREAK.isMining(var24)) {
                                          if (this.breakCrystal.getValue()) {
                                             CombatUtil.attackCrystal(var24, this.rotate.getValue(), false);
                                          }

                                          if (BlockUtil.canPlace(var24, 6.0, this.breakCrystal.getValue())) {
                                             this.blockerPos.add(var24);
                                          } else if (BlockUtil.canReplace(var24) && !BlockUtil.hasEntity(var24, true) && this.getHelper(var24) != null) {
                                             this.blockerPos.add(this.getHelper(var24));
                                          }
                                       }
                                    }
                                 }
                              }
                           }
                        }
                     }

                     if (this.feet.getValue()
                        && this.extend.getValue()
                        && (!this.onlySurround.getValue() || Surround.INSTANCE.isOn() || SelfTrap.INSTANCE.isOn())) {
                        for (double var48 : var2) {
                           for (double var60 : var2) {
                              for (Direction var72 : Direction.values()) {
                                 BlockPos var75;
                                 if (var72 != Direction.UP
                                    && var72 != Direction.DOWN
                                    && !this.isBedrock(
                                       var75 = new BlockPosX(mc.player.getX() + var48, mc.player.getY() + 0.5, mc.player.getZ() + var60).offset(var72)
                                    )) {
                                    for (Direction var82 : Direction.values()) {
                                       BlockPos var83;
                                       if (var82 != Direction.UP
                                          && var82 != Direction.DOWN
                                          && AutoCrystal.INSTANCE.canPlaceCrystal(var83 = var75.offset(var82), true, true)
                                          && (!this.detectMining.getValue() || !CrucifixBeta.BREAK.isMining(var83))) {
                                          if (this.breakCrystal.getValue()) {
                                             CombatUtil.attackCrystal(var83, this.rotate.getValue(), false);
                                          }

                                          if (BlockUtil.canPlace(var83, 6.0, this.breakCrystal.getValue())) {
                                             this.blockerPos.add(var83);
                                          }
                                       }
                                    }
                                 }
                              }
                           }
                        }
                     }

                     this.blockerPos.removeIf(pos -> !BlockUtil.clientCanPlace(pos, true));
                     if (!this.inAirPause.getValue() || mc.player.isOnGround()) {
                        if (!this.blockerPos.isEmpty()) {
                           int var30 = mc.player.getInventory().selectedSlot;
                           int var36 = this.getObsidian();
                           if (var36 != -1) {
                              this.doSwap(var36);

                              for (BlockPos var44 : this.blockerPos) {
                                 if (BlockUtil.canPlace(var44, 6.0, this.breakCrystal.getValue())) {
                                    this.doPlace(var44);
                                 }
                              }

                              if (this.inventorySwap.getValue()) {
                                 this.doSwap(var36);
                                 EntityUtil.syncInventory();
                              } else {
                                 this.doSwap(var30);
                              }
                           }
                        }
                     }
                  }
               }
            }
         }
      }
   }

   public BlockPos getHelper(BlockPos pos) {
      for (Direction var5 : Direction.values()) {
         if (var5 != Direction.UP
            && (!this.detectMining.getValue() || !CrucifixBeta.BREAK.isMining(pos.offset(var5)))
            && BlockUtil.isStrictDirection(pos.offset(var5), var5.getOpposite())
            && BlockUtil.canPlace(pos.offset(var5), 6.0)) {
            return pos.offset(var5);
         }
      }

      return null;
   }

   private boolean crystalHere(BlockPos pos) {
      return BlockUtil.getEndCrystals(new Box(pos)).stream().anyMatch(entity -> entity.getBlockPos().equals(pos));
   }

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

   private void doPlace(BlockPos pos) {
      if (!this.list.contains(pos)) {
         this.list.add(pos);
         if ((double)this.placeProgress < this.blocksPer.getValue()) {
            BlockUtil.placeBlock(pos, this.rotate.getValue());
            this.timer.reset();
            this.placeProgress++;
         }
      }
   }

   private void tryPlaceObsidian(BlockPos pos) {
      if (!this.list.contains(pos)) {
         this.list.add(pos);
         if ((double)this.placeProgress < this.blocksPer.getValue()) {
            if (!this.detectMining.getValue() || !CrucifixBeta.BREAK.isMining(pos)) {
               int var2 = mc.player.getInventory().selectedSlot;
               int var3 = this.getObsidian();
               if (var3 != -1) {
                  this.doSwap(var3);
                  BlockUtil.placeBlock(pos, this.rotate.getValue());
                  this.timer.reset();
                  if (this.inventorySwap.getValue()) {
                     this.doSwap(var3);
                     EntityUtil.syncInventory();
                  } else {
                     this.doSwap(var2);
                  }

                  this.placeProgress++;
               }
            }
         }
      }
   }

   private void doSwap(int slot) {
      if (this.inventorySwap.getValue()) {
         InventoryUtil.inventorySwap(slot, mc.player.getInventory().selectedSlot);
      } else {
         InventoryUtil.switchToSlot(slot);
      }
   }

   private int getObsidian() {
      return this.inventorySwap.getValue() ? InventoryUtil.findBlockInventorySlot(Blocks.OBSIDIAN) : InventoryUtil.findBlock(Blocks.OBSIDIAN);
   }
@Environment(EnvType.CLIENT)
   public static enum Page {
      General,
      Target,
      Check;

}
}
