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.BooleanSetting;
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 AutoPush extends Module {
   public static AutoPush INSTANCE;
   private final BooleanSetting torch = this.add(new BooleanSetting("Torch", false));
   private final BooleanSetting rotate = this.add(new BooleanSetting("Rotation", true));
   private final BooleanSetting yawDeceive = this.add(new BooleanSetting("YawDeceive", true));
   private final BooleanSetting pistonPacket = this.add(new BooleanSetting("PistonPacket", false));
   private final BooleanSetting powerPacket = this.add(new BooleanSetting("PowerPacket", true));
   private final BooleanSetting noEating = this.add(new BooleanSetting("EatingPause", true));
   private final BooleanSetting mine = this.add(new BooleanSetting("Mine", true));
   private final BooleanSetting holePush = this.add(new BooleanSetting("HolePush", false));
   private final BooleanSetting allowWeb = this.add(new BooleanSetting("AllowWeb", true));
   private final SliderSetting updateDelay = this.add(new SliderSetting("Delay", 100, 0, 1000));
   private final BooleanSetting selfGround = this.add(new BooleanSetting("SelfGround", true));
   private final BooleanSetting onlyGround = this.add(new BooleanSetting("OnlyGround", false));
   private final BooleanSetting autoDisable = this.add(new BooleanSetting("AutoDisable", true));
   private final SliderSetting range = this.add(new SliderSetting("Range", 5.0, 0.0, 6.0));
   private final SliderSetting placeRange = this.add(new SliderSetting("PlaceRange", 5.0, 0.0, 6.0));
   private final SliderSetting surroundCheck = this.add(new SliderSetting("SurroundCheck", 2, 0, 4));
   private final BooleanSetting inventory = this.add(new BooleanSetting("InventorySwap", true));
   private final Timer timer = new Timer();
   private final HolePush holePushHelper = new HolePush();

   public AutoPush() {
      super("AutoPush", Module.Category.Combat);
      this.setChinese("\u6d3b\u585e\u63a8\u4eba");
      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() {
      AutoCrystal.INSTANCE.lastBreakTimer.reset();
      if (this.holePush.getValue()) {
         this.holePushHelper.onEnable();
      }
   }

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

   @EventListener
   public void onUpdate(UpdateEvent event) {
      if (this.holePush.getValue()) {
         this.holePushHelper.onUpdate(event);
      }

      if (this.timer.passedMs(this.updateDelay.getValue())) {
         if (!this.selfGround.getValue() || mc.player.isOnGround()) {
            if (this.findBlock(this.getBlockType()) != -1 && this.findClass(PistonBlock.class) != -1) {
               if (!this.noEating.getValue() || !this.shouldPauseForUsingItem()) {
                  if (!Blink.INSTANCE.isOn() || !Blink.INSTANCE.pauseModule.getValue()) {
                     for (PlayerEntity var3 : CombatUtil.getEnemies(this.range.getValue())) {
                        if (this.canPush(var3)) {
                           float[] var4;
                           for (float var8 : var4 = new float[]{-0.25F, 0.0F, 0.25F}) {
                              for (float var12 : var4) {
                                 BlockPosX var13 = new BlockPosX(var3.getX() + (double)var8, var3.getY() + 0.5, var3.getZ() + (double)var12);

                                 for (Direction var17 : Direction.values()) {
                                    BlockPos var18;
                                    if (var17 != Direction.UP
                                       && var17 != Direction.DOWN
                                       && this.isTargetHere(var18 = var13.offset(var17), var3)
                                       && mc.world.canCollide(var3, new Box(var18))) {
                                       if (this.tryPush(var13.offset(var17.getOpposite()), var17)) {
                                          this.timer.reset();
                                          return;
                                       }

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

                           if (!mc.world.canCollide(var3, new Box(new BlockPosX(var3.getX(), var3.getY() + 2.5, var3.getZ())))) {
                              for (Direction var25 : Direction.values()) {
                                 if (var25 != Direction.UP && var25 != Direction.DOWN) {
                                    BlockPos var27 = EntityUtil.getEntityPos(var3).offset(var25);
                                    Box var29 = var3.getBoundingBox()
                                       .offset(new Vec3d((double)var25.getOffsetX(), (double)var25.getOffsetY(), (double)var25.getOffsetZ()));
                                    if (this.getBlock(var27.up()) != Blocks.PISTON_HEAD
                                       && !mc.world.canCollide(var3, var29.offset(0.0, 1.0, 0.0))
                                       && !this.isTargetHere(var27, var3)) {
                                       if (this.tryPush(EntityUtil.getEntityPos(var3).offset(var25.getOpposite()).up(), var25)) {
                                          this.timer.reset();
                                          return;
                                       }

                                       if (this.tryPush(EntityUtil.getEntityPos(var3).offset(var25.getOpposite()), var25)) {
                                          this.timer.reset();
                                          return;
                                       }
                                    }
                                 }
                              }
                           }

                           for (float var26 : var4) {
                              for (float var32 : var4) {
                                 BlockPosX var33 = new BlockPosX(var3.getX() + (double)var26, var3.getY() + 0.5, var3.getZ() + (double)var32);

                                 for (Direction var37 : Direction.values()) {
                                    if (var37 != Direction.UP && var37 != Direction.DOWN && this.isTargetHere(var33.offset(var37), var3)) {
                                       if (this.tryPush(var33.offset(var37.getOpposite()).up(), var37)) {
                                          this.timer.reset();
                                          return;
                                       }

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

   private boolean tryPush(BlockPos piston, Direction direction) {
      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 var3 = false;
            if (BlockUtil.getPlaceSide(piston, this.placeRange.getValue()) != null) {
               CombatUtil.modifyPos = piston;
               CombatUtil.modifyBlockState = Blocks.PISTON.getDefaultState();

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

               for (Direction var33 : Direction.values()) {
                  if (var3) {
                     break;
                  }

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

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

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

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

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

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

                     for (Direction var12 : Direction.values()) {
                        if (this.getBlock(piston.offset(var12)) == this.getBlockType()) {
                           if (this.mine.getValue()) {
                              PacketMine.INSTANCE.mine(piston.offset(var12));
                           }

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

                           return true;
                        }
                     }

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

                           if (this.mine.getValue()) {
                              PacketMine.INSTANCE.mine(piston.offset(var45));
                           }

                           return true;
                        }
                     }

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

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

                     CombatUtil.modifyPos = piston.offset(var38);
                     CombatUtil.modifyBlockState = this.getBlockType().getDefaultState();
                     if (BlockUtil.getPlaceSide(piston) != null) {
                        var19 = var38;
                     }

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

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

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

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

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

                     if (this.rotate.getValue() && this.yawDeceive.getValue()) {
                        CrucifixBeta.ROTATION.lookAt(piston.offset(var40), var40.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 var30 : Direction.values()) {
               if (this.getBlock(piston.offset(var30)) == this.getBlockType()) {
                  if (this.autoDisable.getValue()) {
                     this.disable();
                     return true;
                  }

                  return false;
               }
            }

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

                  return true;
               }
            }
         }

         return false;
      }
   }

   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 canPush(PlayerEntity player) {
      if (this.onlyGround.getValue() && !player.isOnGround()) {
         return false;
      } else if (!this.allowWeb.getValue() && CrucifixBeta.PLAYER.isInWeb(player)) {
         return false;
      } else {
         float[] var2 = new float[]{-0.25F, 0.0F, 0.25F};
         int var3 = 0;
         if (mc.world.canCollide(player, new Box(new BlockPosX(player.getX() + 1.0, player.getY() + 0.5, player.getZ())))) {
            var3++;
         }

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

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

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

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

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

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

         if (!mc.world.canCollide(player, new Box(new BlockPosX(player.getX(), player.getY() + 2.5, player.getZ())))) {
            for (Direction var21 : Direction.values()) {
               if (var21 != Direction.UP && var21 != Direction.DOWN) {
                  BlockPos var22 = EntityUtil.getEntityPos(player).offset(var21);
                  Box var23 = player.getBoundingBox().offset(new Vec3d((double)var21.getOffsetX(), (double)var21.getOffsetY(), (double)var21.getOffsetZ()));
                  if (this.getBlock(var22.up()) != Blocks.PISTON_HEAD
                     && !mc.world.canCollide(player, var23.offset(0.0, 1.0, 0.0))
                     && !this.isTargetHere(var22, player)
                     && mc.world.canCollide(player, new Box(new BlockPosX(player.getX(), player.getY() + 0.5, player.getZ())))) {
                     return true;
                  }
               }
            }
         }

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

   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);
   }
}
