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.MathUtil;
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.core.impl.RotationManager;
import dev.crucifixbeta.mod.modules.Module;
import dev.crucifixbeta.mod.modules.impl.client.ClientSetting;
import dev.crucifixbeta.mod.modules.impl.exploit.Blink;
import dev.crucifixbeta.mod.modules.impl.player.PacketMine;
import dev.crucifixbeta.mod.modules.settings.impl.BindSetting;
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.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.FacingBlock;
import net.minecraft.block.PistonBlock;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;

@Environment(EnvType.CLIENT)
public class HolePush extends Module {
   public static HolePush INSTANCE;
   private static final float[] OFFSETS;
   private static final Direction[] HORIZONTAL_DIRECTIONS;
   private final EnumSetting<HolePush.Page> page = this.add(new EnumSetting<>("Page", HolePush.Page.General));
   private final BooleanSetting torch = this.add(new BooleanSetting("Torch", false, () -> this.page.is(HolePush.Page.General)));
   private final BooleanSetting rotate = this.add(new BooleanSetting("Rotate", true, () -> this.page.is(HolePush.Page.Rotate)));
   private final BooleanSetting yawDeceive = this.add(new BooleanSetting("YawDeceive", true, () -> this.page.is(HolePush.Page.Rotate)));
   private final BooleanSetting allowWeb = this.add(new BooleanSetting("AllowWeb", true, () -> this.page.is(HolePush.Page.Check)));
   public final BindSetting allowKey = this.add(
      new BindSetting("AllowKey", -1, () -> !this.allowWeb.getValue() && this.page.is(HolePush.Page.Check))
   );
   private final BooleanSetting noEating = this.add(new BooleanSetting("EatingPause", true, () -> this.page.is(HolePush.Page.Check)));
   private final BooleanSetting selfGround = this.add(new BooleanSetting("SelfGround", true, () -> this.page.is(HolePush.Page.Check)));
   private final BooleanSetting onlyGround = this.add(new BooleanSetting("OnlyGround", false, () -> this.page.is(HolePush.Page.Check)));
   private final BooleanSetting onlyNoInside = this.add(new BooleanSetting("OnlyNoInside", false, () -> this.page.is(HolePush.Page.Check)));
   private final BooleanSetting onlyInside = this.add(new BooleanSetting("OnlyInside", true, () -> this.page.is(HolePush.Page.Check)));
   private final SliderSetting surroundCheck = this.add(new SliderSetting("SurroundCheck", 0, 0, 4, () -> this.page.is(HolePush.Page.Check)));
   private final BooleanSetting syncCrystal = this.add(new BooleanSetting("SyncCrystal", true, () -> this.page.is(HolePush.Page.Check)));
   private final BooleanSetting pistonPacket = this.add(new BooleanSetting("PistonPacket", true, () -> this.page.is(HolePush.Page.General)));
   private final BooleanSetting powerPacket = this.add(new BooleanSetting("PowerPacket", true, () -> this.page.is(HolePush.Page.General)));
   private final BooleanSetting mine = this.add(new BooleanSetting("Mine", true, () -> this.page.is(HolePush.Page.General)));
   private final BooleanSetting pauseCity = this.add(
      new BooleanSetting("PauseCity", true, () -> this.mine.isOpen() && this.page.is(HolePush.Page.General))
   );
   private final SliderSetting updateDelay = this.add(new SliderSetting("Delay", 200, 0, 1000, () -> this.page.is(HolePush.Page.General)));
   private final BooleanSetting autoDisable = this.add(new BooleanSetting("AutoDisable", true, () -> this.page.is(HolePush.Page.General)));
   private final SliderSetting range = this.add(new SliderSetting("Range", 4.7, 0.0, 6.0, () -> this.page.is(HolePush.Page.General)));
   private final SliderSetting placeRange = this.add(new SliderSetting("PlaceRange", 4.7, 0.0, 6.0, () -> this.page.is(HolePush.Page.General)));
   private final BooleanSetting inventory = this.add(new BooleanSetting("InventorySwap", true, () -> this.page.is(HolePush.Page.General)));
   private final Timer timer = new Timer();

   public HolePush() {
      super("HolePush", Module.Category.Combat);
      this.setChinese("\u5751\u63a8");
      INSTANCE = this;
   }

   public static void pistonFacing(Direction i) {
      if (i == Direction.EAST) {
         CrucifixBeta.ROTATION.snapAt(-90.0F, 5.0F);
      } else if (i == Direction.WEST) {
         CrucifixBeta.ROTATION.snapAt(90.0F, 5.0F);
      } else if (i == Direction.NORTH) {
         CrucifixBeta.ROTATION.snapAt(180.0F, 5.0F);
      } else if (i == Direction.SOUTH) {
         CrucifixBeta.ROTATION.snapAt(0.0F, 5.0F);
      }
   }

   @Override
   public void onEnable() {
      if (AutoCrystal.INSTANCE != null) {
         AutoCrystal.INSTANCE.lastBreakTimer.reset();
      }
   }

   @EventListener
   public void onUpdate(UpdateEvent event) {
      if (this.timer.passedMs(this.updateDelay.getValue())) {
         if (!this.selfGround.getValue() || mc.player.isOnGround()) {
            Block var2 = this.getBlockType();
            int var3 = this.findBlock(var2);
            int var4 = this.findClass(PistonBlock.class);
            if (var3 != -1 && var4 != -1) {
               if (!this.syncCrystal.getValue() || AutoCrystal.INSTANCE == null || AutoCrystal.INSTANCE.crystalPos == null) {
                  if (!this.noEating.getValue() || !this.shouldPauseForUsingItem()) {
                     if (!Blink.INSTANCE.isOn() || !Blink.INSTANCE.pauseModule.getValue()) {
                        for (PlayerEntity var6 : CombatUtil.getEnemies(this.range.getValue())) {
                           if (this.canPush(var6)) {
                              BlockPos var7 = EntityUtil.getEntityPos(var6);

                              for (Direction var11 : HORIZONTAL_DIRECTIONS) {
                                 BlockPos var12;
                                 if (this.isTargetHere(var12 = var7.offset(var11), var6) && mc.world.canCollide(var6, new Box(var12))) {
                                    if (this.tryPush(var7.offset(var11.getOpposite()), var11)) {
                                       this.timer.reset();
                                       return;
                                    }

                                    if (this.tryPush(var7.offset(var11.getOpposite()).up(), var11)) {
                                       this.timer.reset();
                                       return;
                                    }
                                 }
                              }

                              for (float var31 : OFFSETS) {
                                 for (float var15 : OFFSETS) {
                                    BlockPosX var16 = new BlockPosX(var6.getX() + (double)var31, var6.getY() + 0.5, var6.getZ() + (double)var15);

                                    for (Direction var20 : HORIZONTAL_DIRECTIONS) {
                                       BlockPos var21;
                                       if (this.isTargetHere(var21 = var16.offset(var20), var6) && mc.world.canCollide(var6, new Box(var21))) {
                                          if (this.tryPush(var16.offset(var20.getOpposite()), var20)) {
                                             this.timer.reset();
                                             return;
                                          }

                                          if (this.tryPush(var16.offset(var20.getOpposite()).up(), var20)) {
                                             this.timer.reset();
                                             return;
                                          }
                                       }
                                    }
                                 }
                              }

                              if (!mc.world.canCollide(var6, new Box(new BlockPosX(var6.getX(), var6.getY() + 2.5, var6.getZ())))) {
                                 for (Direction var32 : HORIZONTAL_DIRECTIONS) {
                                    BlockPos var35 = var7.offset(var32);
                                    Box var37 = var6.getBoundingBox()
                                       .offset(new Vec3d((double)var32.getOffsetX(), (double)var32.getOffsetY(), (double)var32.getOffsetZ()));
                                    if (this.getBlock(var35.up()) != Blocks.PISTON_HEAD
                                       && !mc.world.canCollide(var6, var37.offset(0.0, 1.0, 0.0))
                                       && !this.isTargetHere(var35, var6)) {
                                       if (this.tryPush(var7.offset(var32.getOpposite()).up(), var32)) {
                                          this.timer.reset();
                                          return;
                                       }

                                       if (this.tryPush(var7.offset(var32.getOpposite()), var32)) {
                                          this.timer.reset();
                                          return;
                                       }
                                    }
                                 }
                              }

                              for (float var33 : OFFSETS) {
                                 for (float var40 : OFFSETS) {
                                    BlockPosX var41 = new BlockPosX(var6.getX() + (double)var33, var6.getY() + 0.5, var6.getZ() + (double)var40);

                                    for (Direction var45 : HORIZONTAL_DIRECTIONS) {
                                       if (this.isTargetHere(var41.offset(var45), var6)) {
                                          if (this.tryPush(var41.offset(var45.getOpposite()).up(), var45)) {
                                             this.timer.reset();
                                             return;
                                          }

                                          if (this.tryPush(var41.offset(var45.getOpposite()), var45)) {
                                             this.timer.reset();
                                             return;
                                          }
                                       }
                                    }
                                 }
                              }
                           }
                        }
                     }
                  }
               }
            } else {
               if (this.autoDisable.getValue()) {
                  this.disable();
               }
            }
         }
      }
   }

   private boolean tryPush(BlockPos piston, Direction direction) {
      Block var3 = this.getBlockType();
      if (!this.tryUseCombatTask()) {
         return false;
      } else if (!mc.world.isAir(piston.offset(direction))) {
         return false;
      } else {
         if (this.isTrueFacing(piston, direction) && this.facingCheck(piston) && BlockUtil.clientCanPlace(piston, false)) {
            boolean var4 = false;
            if (BlockUtil.getPlaceSide(piston, this.placeRange.getValue()) != null) {
               CombatUtil.modifyPos = piston;
               CombatUtil.modifyBlockState = Blocks.PISTON.getDefaultState();

               for (Direction var8 : Direction.values()) {
                  if (this.getBlock(piston.offset(var8)) == var3) {
                     var4 = true;
                     break;
                  }
               }

               for (Direction var34 : Direction.values()) {
                  if (var4) {
                     break;
                  }

                  if (BlockUtil.canPlace(piston.offset(var34), this.placeRange.getValue())) {
                     var4 = true;
                  }
               }

               CombatUtil.modifyPos = null;
               if (var4) {
                  int var18 = this.findClass(PistonBlock.class);
                  Direction var23 = BlockUtil.getPlaceSide(piston, this.placeRange.getValue());
                  if (var23 != null) {
                     if (this.rotate.getValue()) {
                        CrucifixBeta.ROTATION.lookAt(piston.offset(var23), var23.getOpposite());
                     }

                     if (this.yawDeceive.getValue()) {
                        pistonFacing(direction.getOpposite());
                     }

                     int var29 = mc.player.getInventory().selectedSlot;
                     this.doSwap(var18);
                     BlockUtil.placeBlock(piston, false, this.pistonPacket.getValue());
                     if (this.inventory.getValue()) {
                        this.doSwap(var18);
                        EntityUtil.syncInventory();
                     } else {
                        this.doSwap(var29);
                     }

                     if (this.rotate.getValue() && this.yawDeceive.getValue()) {
                        CrucifixBeta.ROTATION.lookAt(piston.offset(var23), var23.getOpposite());
                     }

                     if (this.rotate.getValue()) {
                        CrucifixBeta.ROTATION.snapBack();
                     }

                     for (Direction var11 : Direction.values()) {
                        if (this.getBlock(piston.offset(var11)) == var3) {
                           if (this.shouldMine()) {
                              PacketMine.INSTANCE.mine(piston.offset(var11));
                           }

                           if (this.autoDisable.getValue()) {
                              this.disable();
                           }

                           return true;
                        }
                     }

                     for (Direction var46 : Direction.values()) {
                        if ((var46 != Direction.UP || !this.torch.getValue()) && BlockUtil.canPlace(piston.offset(var46), this.placeRange.getValue())) {
                           int var12 = mc.player.getInventory().selectedSlot;
                           int var13 = this.findBlock(var3);
                           this.doSwap(var13);
                           BlockUtil.placeBlock(piston.offset(var46), this.rotate.getValue(), this.powerPacket.getValue());
                           if (this.inventory.getValue()) {
                              this.doSwap(var13);
                              EntityUtil.syncInventory();
                           } else {
                              this.doSwap(var12);
                           }

                           if (this.shouldMine()) {
                              PacketMine.INSTANCE.mine(piston.offset(var46));
                           }

                           return true;
                        }
                     }

                     return true;
                  }
               }
            } else {
               Direction var19 = null;

               for (Direction var41 : Direction.values()) {
                  if (var41 != Direction.UP || !this.torch.getValue()) {
                     if (var19 != null) {
                        break;
                     }

                     CombatUtil.modifyPos = piston.offset(var41);
                     CombatUtil.modifyBlockState = var3.getDefaultState();
                     if (BlockUtil.getPlaceSide(piston, this.placeRange.getValue()) != null) {
                        var19 = var41;
                     }

                     CombatUtil.modifyPos = null;
                     if (var19 != null && !BlockUtil.canPlace(piston.offset(var19))) {
                        var19 = null;
                     }
                  }
               }

               if (var19 != null) {
                  int var27 = mc.player.getInventory().selectedSlot;
                  int var33 = this.findBlock(var3);
                  this.doSwap(var33);
                  BlockUtil.placeBlock(piston.offset(var19), this.rotate.getValue(), this.powerPacket.getValue());
                  if (this.inventory.getValue()) {
                     this.doSwap(var33);
                     EntityUtil.syncInventory();
                  } else {
                     this.doSwap(var27);
                  }

                  CombatUtil.modifyPos = piston.offset(var19);
                  CombatUtil.modifyBlockState = var3.getDefaultState();
                  int var39 = this.findClass(PistonBlock.class);
                  Direction var43 = BlockUtil.getPlaceSide(piston, this.placeRange.getValue());
                  if (var43 != null) {
                     if (this.rotate.getValue()) {
                        CrucifixBeta.ROTATION.lookAt(piston.offset(var43), var43.getOpposite());
                     }

                     if (this.yawDeceive.getValue()) {
                        pistonFacing(direction.getOpposite());
                     }

                     int var45 = mc.player.getInventory().selectedSlot;
                     this.doSwap(var39);
                     BlockUtil.placeBlock(piston, false, this.pistonPacket.getValue());
                     if (this.inventory.getValue()) {
                        this.doSwap(var39);
                        EntityUtil.syncInventory();
                     } else {
                        this.doSwap(var45);
                     }

                     if (this.rotate.getValue() && this.yawDeceive.getValue()) {
                        CrucifixBeta.ROTATION.lookAt(piston.offset(var43), var43.getOpposite());
                     }

                     if (this.rotate.getValue()) {
                        CrucifixBeta.ROTATION.snapBack();
                     }
                  }

                  CombatUtil.modifyPos = null;
                  return true;
               }
            }
         }

         if (mc.world.getBlockState(piston).getBlock() instanceof PistonBlock && this.getBlockState(piston).get(FacingBlock.FACING) == direction) {
            for (Direction var31 : Direction.values()) {
               if (this.getBlock(piston.offset(var31)) == var3) {
                  if (this.autoDisable.getValue()) {
                     this.disable();
                     return true;
                  }

                  return false;
               }
            }

            for (Direction var32 : Direction.values()) {
               if ((var32 != Direction.UP || !this.torch.getValue()) && BlockUtil.canPlace(piston.offset(var32), this.placeRange.getValue())) {
                  int var38 = mc.player.getInventory().selectedSlot;
                  int var42 = this.findBlock(var3);
                  this.doSwap(var42);
                  BlockUtil.placeBlock(piston.offset(var32), this.rotate.getValue(), this.powerPacket.getValue());
                  if (this.inventory.getValue()) {
                     this.doSwap(var42);
                     EntityUtil.syncInventory();
                  } else {
                     this.doSwap(var38);
                  }

                  return true;
               }
            }
         }

         return false;
      }
   }

   @Override
   public String getInfo() {
      if (this.syncCrystal.getValue() && AutoCrystal.INSTANCE != null && AutoCrystal.INSTANCE.crystalPos != null) {
         return "WaitSync";
      } else {
         return !this.allowWeb.getValue() && !this.allowKey.isPressed() ? "SmartPush" : "CrazyDog";
      }
   }

   private boolean shouldMine() {
      if (!this.mine.getValue()) {
         return false;
      } else if (!this.pauseCity.getValue()) {
         return true;
      } else {
         BlockPos var1 = PacketMine.getBreakPos();
         return var1 != null && var1.equals(PacketMine.secondPos);
      }
   }

   private boolean facingCheck(BlockPos pos) {
      if (!ClientSetting.INSTANCE.lowVersion.getValue()) {
         return true;
      } else {
         Direction var2 = MathUtil.getDirectionFromEntityLiving(pos, mc.player);
         return var2 != Direction.UP && var2 != Direction.DOWN;
      }
   }

   private boolean isTrueFacing(BlockPos pos, Direction facing) {
      if (this.yawDeceive.getValue()) {
         return true;
      } else {
         Direction var3 = BlockUtil.getPlaceSide(pos);
         if (var3 == null) {
            return false;
         } else {
            Vec3d var4 = new Vec3d(
               (double)pos.getX() + 0.5 + (double)var3.getVector().getX() * 0.5,
               (double)pos.getY() + 0.5 + (double)var3.getVector().getY() * 0.5,
               (double)pos.getZ() + 0.5 + (double)var3.getVector().getZ() * 0.5
            );
            float[] var5 = RotationManager.getRotation(var4);
            return MathUtil.getFacingOrder(var5[0], var5[1]).getOpposite() == facing;
         }
      }
   }

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

   public int findBlock(Block blockIn) {
      return this.inventory.getValue() ? InventoryUtil.findBlockInventorySlot(blockIn) : InventoryUtil.findBlock(blockIn);
   }

   public int findClass(Class<?> clazz) {
      return this.inventory.getValue() ? InventoryUtil.findClassInventorySlot(clazz) : InventoryUtil.findClass(clazz);
   }

   private boolean burrowUpdate(PlayerEntity player) {
      for (float var5 : new float[]{0.0F, 0.3F, -0.3F}) {
         for (float var9 : new float[]{0.0F, 0.3F, -0.3F}) {
            BlockPosX var10 = new BlockPosX(player.getX() + (double)var5, player.getY() + 1.5, player.getZ() + (double)var9);
            Block var11 = mc.world.getBlockState(var10).getBlock();
            if (new Box(var10).intersects(player.getBoundingBox())
               && (var11 == Blocks.OBSIDIAN || var11 == Blocks.RESPAWN_ANCHOR || var11 == Blocks.BEDROCK || var11 == Blocks.ENDER_CHEST)) {
               return true;
            }
         }
      }

      return false;
   }

   private Boolean canPush(PlayerEntity player) {
      if (this.onlyGround.getValue() && !player.isOnGround()) {
         return false;
      } else if (!this.allowWeb.getValue() && CrucifixBeta.PLAYER.isInWeb(player) && !this.allowKey.isPressed()) {
         return false;
      } else if (this.onlyNoInside.getValue() && this.burrowUpdate(player)) {
         return false;
      } else if (this.onlyInside.getValue() && !this.burrowUpdate(player)) {
         return false;
      } else {
         int var2 = 0;
         if (mc.world.canCollide(player, new Box(new BlockPosX(player.getX() + 1.0, player.getY() + 0.5, player.getZ())))) {
            var2++;
         }

         if (mc.world.canCollide(player, new Box(new BlockPosX(player.getX() - 1.0, player.getY() + 0.5, player.getZ())))) {
            var2++;
         }

         if (mc.world.canCollide(player, new Box(new BlockPosX(player.getX(), player.getY() + 0.5, player.getZ() + 1.0)))) {
            var2++;
         }

         if (mc.world.canCollide(player, new Box(new BlockPosX(player.getX(), player.getY() + 0.5, player.getZ() - 1.0)))) {
            var2++;
         }

         Box var3 = new Box(new BlockPosX(player.getX(), player.getY() + 0.5, player.getZ()));

         for (float var7 : OFFSETS) {
            for (float var11 : OFFSETS) {
               BlockPosX var12 = new BlockPosX(player.getX() + (double)var7, player.getY() + 0.5, player.getZ() + (double)var11);

               for (Direction var16 : HORIZONTAL_DIRECTIONS) {
                  BlockPos var17;
                  if (this.isTargetHere(var17 = var12.offset(var16), player)) {
                     if (mc.world.canCollide(player, new Box(var17))) {
                        return true;
                     }

                     if ((double)var2 > this.surroundCheck.getValue() - 1.0) {
                        return true;
                     }
                  }
               }
            }
         }

         if (!mc.world.canCollide(player, new Box(new BlockPosX(player.getX(), player.getY() + 2.5, player.getZ())))) {
            BlockPos var18 = EntityUtil.getEntityPos(player);

            for (Direction var22 : HORIZONTAL_DIRECTIONS) {
               BlockPos var23 = var18.offset(var22);
               Box var24 = player.getBoundingBox().offset(new Vec3d((double)var22.getOffsetX(), (double)var22.getOffsetY(), (double)var22.getOffsetZ()));
               if (this.getBlock(var23.up()) != Blocks.PISTON_HEAD
                  && !mc.world.canCollide(player, var24.offset(0.0, 1.0, 0.0))
                  && !this.isTargetHere(var23, player)
                  && mc.world.canCollide(player, var3)) {
                  return true;
               }
            }
         }

         return (double)var2 > this.surroundCheck.getValue() - 1.0
            || CrucifixBeta.HOLE.isHard(new BlockPosX(player.getX(), player.getY() + 0.5, player.getZ()));
      }
   }

   private boolean isTargetHere(BlockPos pos, Entity target) {
      return new Box(pos).intersects(target.getBoundingBox());
   }

   private Block getBlock(BlockPos pos) {
      return mc.world.getBlockState(pos).getBlock();
   }

   private Block getBlockType() {
      return this.torch.getValue() ? Blocks.REDSTONE_TORCH : Blocks.REDSTONE_BLOCK;
   }

   private BlockState getBlockState(BlockPos pos) {
      return mc.world.getBlockState(pos);
   }

   static {

      OFFSETS = new float[]{-0.25F, 0.0F, 0.25F};
      HORIZONTAL_DIRECTIONS = new Direction[]{Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST};
   }
@Environment(EnvType.CLIENT)
   public static enum Page {
      General,
      Rotate,
      Check;

}
}
