package dev.crucifixbeta.api.utils.world;

import dev.crucifixbeta.CrucifixBeta;
import dev.crucifixbeta.api.utils.Wrapper;
import dev.crucifixbeta.api.utils.math.MathUtil;
import dev.crucifixbeta.api.utils.player.EntityUtil;
import dev.crucifixbeta.mod.modules.Module;
import dev.crucifixbeta.mod.modules.impl.client.AntiCheat;
import dev.crucifixbeta.mod.modules.impl.client.ClientSetting;
import dev.crucifixbeta.mod.modules.impl.combat.AutoCrystal;
import dev.crucifixbeta.mod.modules.impl.combat.AutoWeb;
import dev.crucifixbeta.mod.modules.impl.player.AirPlace;
import dev.crucifixbeta.mod.modules.settings.enums.Placement;
import dev.crucifixbeta.mod.modules.settings.enums.SwingSide;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.AbstractPressurePlateBlock;
import net.minecraft.block.AnvilBlock;
import net.minecraft.block.BedBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.BlockWithEntity;
import net.minecraft.block.Blocks;
import net.minecraft.block.ButtonBlock;
import net.minecraft.block.CartographyTableBlock;
import net.minecraft.block.CraftingTableBlock;
import net.minecraft.block.DoorBlock;
import net.minecraft.block.FenceGateBlock;
import net.minecraft.block.GrindstoneBlock;
import net.minecraft.block.LoomBlock;
import net.minecraft.block.NoteBlock;
import net.minecraft.block.StonecutterBlock;
import net.minecraft.block.TrapdoorBlock;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.minecraft.entity.ExperienceOrbEntity;
import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.decoration.ArmorStandEntity;
import net.minecraft.entity.decoration.EndCrystalEntity;
import net.minecraft.entity.projectile.ArrowEntity;
import net.minecraft.entity.projectile.thrown.ExperienceBottleEntity;
import net.minecraft.item.Items;
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket;
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockCollisionSpliterator;
import net.minecraft.world.World;
import net.minecraft.world.chunk.WorldChunk;
import org.jetbrains.annotations.Nullable;

@Environment(EnvType.CLIENT)
public class BlockUtil implements Wrapper {
   public static final List<BlockPos> placedPos = new ArrayList<>();
   private static final double MIN_EYE_HEIGHT = 0.4;
   private static final double MAX_EYE_HEIGHT = 1.62;
   private static final double MOVEMENT_THRESHOLD = 2.0E-4;

   public static boolean canPlace(BlockPos pos) {
      return canPlace(pos, 1000.0);
   }

   public static boolean canPlace(BlockPos pos, double getDistance) {
      if (getPlaceSide(pos, getDistance) == null) {
         return false;
      } else {
         return !canReplace(pos) ? false : !hasEntity(pos, false);
      }
   }

   public static boolean canPlace(BlockPos pos, double getDistance, boolean ignoreCrystal) {
      if (getPlaceSide(pos, getDistance) == null) {
         return false;
      } else {
         return !canReplace(pos) ? false : !hasEntity(pos, ignoreCrystal);
      }
   }

   public static boolean clientCanPlace(BlockPos pos, boolean ignoreCrystal) {
      return !canReplace(pos) ? false : !hasEntity(pos, ignoreCrystal);
   }

   public static List<Entity> getEntities(Box box) {
      ArrayList var1 = new ArrayList();

      for (Entity var3 : CrucifixBeta.THREAD.getEntities()) {
         if (var3 != null && (!(var3 instanceof ArmorStandEntity) || !AntiCheat.INSTANCE.ignoreArmorStand.getValue()) && var3.getBoundingBox().intersects(box)) {
            var1.add(var3);
         }
      }

      return var1;
   }

   public static List<EndCrystalEntity> getEndCrystals(Box box) {
      ArrayList var1 = new ArrayList();

      for (Entity var3 : CrucifixBeta.THREAD.getEntities()) {
         EndCrystalEntity var4;
         if (var3 instanceof EndCrystalEntity && (var4 = (EndCrystalEntity)var3).getBoundingBox().intersects(box)) {
            var1.add(var4);
         }
      }

      return var1;
   }

   public static boolean hasEntity(BlockPos pos, boolean ignoreCrystal) {
      return hasEntity(new Box(pos), ignoreCrystal);
   }

   public static boolean hasEntity(Box box, boolean ignoreCrystal) {
      for (Entity var3 : getEntities(box)) {
         if (var3.isAlive()
            && !(var3 instanceof ItemEntity)
            && !(var3 instanceof ExperienceOrbEntity)
            && !(var3 instanceof ExperienceBottleEntity)
            && !(var3 instanceof ArrowEntity)
            && (
               !ignoreCrystal
                  || !(var3 instanceof EndCrystalEntity)
                  || !(mc.player.getEyePos().distanceTo(MathUtil.getClosestPoint(var3)) <= AntiCheat.INSTANCE.ieRange.getValue())
            )) {
            return true;
         }
      }

      return false;
   }

   public static boolean hasCrystal(BlockPos pos) {
      for (Entity var2 : getEndCrystals(new Box(pos))) {
         if (var2.isAlive() && var2 instanceof EndCrystalEntity) {
            return true;
         }
      }

      return false;
   }

   public static boolean noEntityBlockCrystal(BlockPos pos, boolean ignoreCrystal) {
      return noEntityBlockCrystal(pos, ignoreCrystal, false);
   }

   public static boolean noEntityBlockCrystal(BlockPos pos, boolean ignoreCrystal, boolean ignoreItem) {
      for (Entity var4 : getEntities(new Box(pos))) {
         if (var4.isAlive()
            && (!ignoreItem || !(var4 instanceof ItemEntity))
            && (
               !ignoreCrystal
                  || !(var4 instanceof EndCrystalEntity)
                  || !(mc.player.getEyePos().distanceTo(MathUtil.getClosestPoint(var4)) <= AntiCheat.INSTANCE.ieRange.getValue())
            )) {
            return false;
         }
      }

      return true;
   }

   public static boolean canPlaceCrystal(BlockPos pos) {
      BlockPos var1 = pos.down();
      BlockPos var2 = var1.up();
      return (getBlock(var1) == Blocks.BEDROCK || getBlock(var1) == Blocks.OBSIDIAN)
         && getClickSideStrict(var1) != null
         && mc.world.isAir(var2)
         && noEntityBlockCrystal(var2, false)
         && noEntityBlockCrystal(var2.up(), false)
         && (!ClientSetting.INSTANCE.lowVersion.getValue() || mc.world.isAir(var2.up()));
   }

   public static void placeCrystal(BlockPos pos, boolean rotate) {
      boolean var2 = mc.player.getOffHandStack().getItem() == Items.END_CRYSTAL;
      BlockPos var3 = pos.down();
      Direction var4 = getClickSide(var3);
      Vec3d var5 = var3.toCenterPos().add((double)var4.getVector().getX() * 0.5, (double)var4.getVector().getY() * 0.5, (double)var4.getVector().getZ() * 0.5);
      if (rotate) {
         CrucifixBeta.ROTATION.lookAt(var5);
      }

      clickBlock(var3, var4, false, var2 ? Hand.OFF_HAND : Hand.MAIN_HAND);
   }

   public static void placeBlock(BlockPos pos, boolean rotate) {
      placeBlock(pos, rotate, AntiCheat.INSTANCE.packetPlace.getValue());
   }

   public static void placeBlock(BlockPos pos, boolean rotate, boolean packet) {
      if (allowAirPlace()) {
         placedPos.add(pos);
         airPlace(pos, rotate, Hand.MAIN_HAND, packet);
      } else {
         Direction var3 = getPlaceSide(pos);
         if (var3 != null) {
            placedPos.add(pos);
            clickBlock(pos.offset(var3), var3.getOpposite(), rotate, Hand.MAIN_HAND, packet);
         }
      }
   }

   public static void clickBlock(BlockPos pos, Direction side, boolean rotate) {
      clickBlock(pos, side, rotate, Hand.MAIN_HAND);
   }

   public static void clickBlock(BlockPos pos, Direction side, boolean rotate, Hand hand) {
      clickBlock(pos, side, rotate, hand, AntiCheat.INSTANCE.packetPlace.getValue());
   }

   public static void clickBlock(BlockPos pos, Direction side, boolean rotate, boolean packet) {
      clickBlock(pos, side, rotate, Hand.MAIN_HAND, packet);
   }

   public static void clickBlock(BlockPos pos, Direction side, boolean rotate, Hand hand, boolean packet) {
      Vec3d var5 = new Vec3d(
         (double)pos.getX() + 0.5 + (double)side.getVector().getX() * 0.5,
         (double)pos.getY() + 0.5 + (double)side.getVector().getY() * 0.5,
         (double)pos.getZ() + 0.5 + (double)side.getVector().getZ() * 0.5
      );
      if (rotate) {
         CrucifixBeta.ROTATION.lookAt(var5);
      }

      EntityUtil.swingHand(hand, AntiCheat.INSTANCE.interactSwing.getValue());
      BlockHitResult var6 = new BlockHitResult(var5, side, pos, false);
      if (packet) {
         Module.sendSequencedPacket(id -> new PlayerInteractBlockC2SPacket(hand, var6, id));
      } else {
         mc.interactionManager.interactBlock(mc.player, hand, var6);
      }

      mc.itemUseCooldown = 4;
      if (rotate) {
         CrucifixBeta.ROTATION.snapBack();
      }
   }

   public static void clickBlock(BlockPos pos, Direction side, boolean rotate, Hand hand, SwingSide swingSide) {
      Vec3d var5 = new Vec3d(
         (double)pos.getX() + 0.5 + (double)side.getVector().getX() * 0.5,
         (double)pos.getY() + 0.5 + (double)side.getVector().getY() * 0.5,
         (double)pos.getZ() + 0.5 + (double)side.getVector().getZ() * 0.5
      );
      if (rotate) {
         CrucifixBeta.ROTATION.lookAt(var5);
      }

      EntityUtil.swingHand(hand, swingSide);
      BlockHitResult var6 = new BlockHitResult(var5, side, pos, false);
      Module.sendSequencedPacket(id -> new PlayerInteractBlockC2SPacket(hand, var6, id));
      mc.itemUseCooldown = 4;
      if (rotate) {
         CrucifixBeta.ROTATION.snapBack();
      }
   }

   public static void airPlace(BlockPos pos, boolean rotate) {
      airPlace(pos, rotate, Hand.MAIN_HAND, AntiCheat.INSTANCE.packetPlace.getValue());
   }

   public static void airPlace(BlockPos pos, boolean rotate, Hand hand, boolean packet) {
      boolean var4 = hand == Hand.MAIN_HAND && AirPlace.INSTANCE.grimBypass.getValue();
      if (var4) {
         mc.getNetworkHandler().sendPacket(new PlayerActionC2SPacket(Action.SWAP_ITEM_WITH_OFFHAND, new BlockPos(0, 0, 0), Direction.DOWN));
         hand = Hand.OFF_HAND;
      }

      Direction var6 = getClickSide(pos);
      Vec3d var7 = new Vec3d(
         (double)pos.getX() + 0.5 + (double)var6.getVector().getX() * 0.5,
         (double)pos.getY() + 0.5 + (double)var6.getVector().getY() * 0.5,
         (double)pos.getZ() + 0.5 + (double)var6.getVector().getZ() * 0.5
      );
      if (rotate) {
         CrucifixBeta.ROTATION.lookAt(var7);
      }

      EntityUtil.swingHand(hand, AntiCheat.INSTANCE.interactSwing.getValue());
      BlockHitResult var8 = new BlockHitResult(var7, var6, pos, false);
      if (packet) {
         Hand var9 = hand;
         Module.sendSequencedPacket(id -> new PlayerInteractBlockC2SPacket(var9, var8, id));
      } else {
         mc.interactionManager.interactBlock(mc.player, hand, var8);
      }

      mc.itemUseCooldown = 4;
      if (rotate) {
         CrucifixBeta.ROTATION.snapBack();
      }

      if (var4) {
         mc.getNetworkHandler().sendPacket(new PlayerActionC2SPacket(Action.SWAP_ITEM_WITH_OFFHAND, new BlockPos(0, 0, 0), Direction.DOWN));
      }
   }

   public static double distanceToXZ(double x, double z, double x2, double z2) {
      double var8 = x2 - x;
      double var10 = z2 - z;
      return Math.sqrt(var8 * var8 + var10 * var10);
   }

   public static double distanceToXZ(double x, double z) {
      return distanceToXZ(x, z, mc.player.getX(), mc.player.getZ());
   }

   public static Direction getPlaceSide(BlockPos pos) {
      if (allowAirPlace()) {
         return getClickSide(pos);
      } else {
         double var1 = Double.MAX_VALUE;
         Direction var3 = null;

         for (Direction var7 : Direction.values()) {
            double var8;
            if (canClick(pos.offset(var7))
               && !canReplace(pos.offset(var7))
               && isStrictDirection(pos.offset(var7), var7.getOpposite())
               && !(
                  (
                        var8 = mc.player
                           .getEyePos()
                           .squaredDistanceTo(
                              pos.toCenterPos()
                                 .add((double)var7.getVector().getX() * 0.5, (double)var7.getVector().getY() * 0.5, (double)var7.getVector().getZ() * 0.5)
                           )
                     )
                     > var1
               )) {
               var3 = var7;
               var1 = var8;
            }
         }

         return var3;
      }
   }

   public static Direction getBestNeighboring(BlockPos pos, Direction facing) {
      Direction var2 = null;
      double var3 = 0.0;

      for (Direction var8 : Direction.values()) {
         if ((facing == null || !pos.offset(var8).equals(pos.offset(facing, -1)))
            && var8 != Direction.DOWN
            && getPlaceSide(pos) != null
            && (var2 == null || mc.player.getEyePos().squaredDistanceTo(pos.offset(var8).toCenterPos()) < var3)) {
            var2 = var8;
            var3 = mc.player.getEyePos().squaredDistanceTo(pos.offset(var8).toCenterPos());
         }
      }

      return var2;
   }

   public static Direction getPlaceSide(BlockPos pos, double reachDistance) {
      if (allowAirPlace()) {
         Direction var3 = getClickSide(pos);
         double var4 = mc.player
            .getEyePos()
            .squaredDistanceTo(
               pos.toCenterPos().add((double)var3.getVector().getX() * 0.5, (double)var3.getVector().getY() * 0.5, (double)var3.getVector().getZ() * 0.5)
            );
         return Math.sqrt(var4) > reachDistance ? null : Direction.DOWN;
      } else {
         double var6 = Double.MAX_VALUE;
         Direction var8 = null;

         for (Direction var12 : Direction.values()) {
            double var13;
            if (canClick(pos.offset(var12))
               && !canReplace(pos.offset(var12))
               && isStrictDirection(pos.offset(var12), var12.getOpposite())
               && !(
                  Math.sqrt(
                        var13 = mc.player
                           .getEyePos()
                           .squaredDistanceTo(
                              pos.toCenterPos()
                                 .add((double)var12.getVector().getX() * 0.5, (double)var12.getVector().getY() * 0.5, (double)var12.getVector().getZ() * 0.5)
                           )
                     )
                     > reachDistance
               )
               && !(var13 > var6)) {
               var8 = var12;
               var6 = var13;
            }
         }

         return var8;
      }
   }

   public static Direction getClickSide(BlockPos pos) {
      Direction var1 = Direction.UP;
      double var2 = Double.MAX_VALUE;

      for (Direction var7 : Direction.values()) {
         double var8;
         if (isStrictDirection(pos, var7) && !((var8 = mc.player.getEyePos().squaredDistanceTo(pos.offset(var7).toCenterPos())) > var2)) {
            var1 = var7;
            var2 = var8;
         }
      }

      return var1;
   }

   public static Direction getClickSideStrict(BlockPos pos) {
      Direction var1 = null;
      double var2 = Double.MAX_VALUE;

      for (Direction var7 : Direction.values()) {
         double var8;
         if (isStrictDirection(pos, var7) && !((var8 = mc.player.getEyePos().squaredDistanceTo(pos.offset(var7).toCenterPos())) > var2)) {
            var1 = var7;
            var2 = var8;
         }
      }

      return var1;
   }

   public static boolean isStrictDirection(BlockPos pos, Direction side, double reachDistance) {
      double var4 = mc.player
         .getEyePos()
         .squaredDistanceTo(
            pos.toCenterPos().add((double)side.getVector().getX() * 0.5, (double)side.getVector().getY() * 0.5, (double)side.getVector().getZ() * 0.5)
         );
      return Math.sqrt(var4) > reachDistance ? false : isStrictDirection(pos, side);
   }

   public static boolean isStrictDirection(BlockPos pos, Direction side) {
      switch ((Placement)AntiCheat.INSTANCE.placement.getValue()) {
         case Vanilla:
            return true;
         case Legit:
            return EntityUtil.canSee(pos, side);
         case Grim:
            return grimStrictDirectionCheck(pos, side, mc.world, mc.player);
         case NCP:
            if (mc.world.getBlockState(pos.offset(side)).isFullCube(mc.world, pos.offset(side))) {
               return false;
            }

            Vec3d var2 = mc.player.getEyePos();
            Vec3d var3 = pos.toCenterPos();
            ArrayList var4 = new ArrayList();
            var4.addAll(checkAxis(var2.x - var3.x, Direction.WEST, Direction.EAST, false));
            var4.addAll(checkAxis(var2.y - var3.y, Direction.DOWN, Direction.UP, true));
            var4.addAll(checkAxis(var2.z - var3.z, Direction.NORTH, Direction.SOUTH, false));
            return var4.contains(side);
         default:
            return true;
      }
   }

   public static boolean grimStrictDirectionCheck(BlockPos pos, Direction direction, ClientWorld level, ClientPlayerEntity player) {
      Box var4 = getCombinedBox(pos, level);
      Box var5 = new Box(player.getX(), player.getY() + 0.4, player.getZ(), player.getX(), player.getY() + 1.62, player.getZ()).expand(2.0E-4);
      if (isIntersected(var5, var4)) {
         return true;
      } else {
         switch (direction) {
            case NORTH:
               if (!(var5.minZ > var4.minZ)) {
                  return true;
               }
               break;
            case SOUTH:
               if (!(var5.maxZ < var4.maxZ)) {
                  return true;
               }
               break;
            case EAST:
               if (!(var5.maxX < var4.maxX)) {
                  return true;
               }
               break;
            case WEST:
               if (!(var5.minX > var4.minX)) {
                  return true;
               }
               break;
            case UP:
               if (!(var5.maxY < var4.maxY)) {
                  return true;
               }
               break;
            case DOWN:
               if (!(var5.minY > var4.minY)) {
                  return true;
               }
               break;
            default:
               throw new MatchException(null, null);
         }

         return false;
      }
   }

   private static Box getCombinedBox(BlockPos pos, World level) {
      VoxelShape var2 = level.getBlockState(pos).getCollisionShape(level, pos).offset((double)pos.getX(), (double)pos.getY(), (double)pos.getZ());
      Box var3 = new Box(pos);

      for (Box var5 : var2.getBoundingBoxes()) {
         double var6 = Math.max(var5.minX, var3.minX);
         double var8 = Math.max(var5.minY, var3.minY);
         double var10 = Math.max(var5.minZ, var3.minZ);
         double var12 = Math.min(var5.maxX, var3.maxX);
         double var14 = Math.min(var5.maxY, var3.maxY);
         double var16 = Math.min(var5.maxZ, var3.maxZ);
         var3 = new Box(var6, var8, var10, var12, var14, var16);
      }

      return var3;
   }

   private static boolean isIntersected(Box bb, Box other) {
      return other.maxX - 1.0E-7 > bb.minX
         && other.minX + 1.0E-7 < bb.maxX
         && other.maxY - 1.0E-7 > bb.minY
         && other.minY + 1.0E-7 < bb.maxY
         && other.maxZ - 1.0E-7 > bb.minZ
         && other.minZ + 1.0E-7 < bb.maxZ;
   }

   public static ArrayList<Direction> checkAxis(double diff, Direction negativeSide, Direction positiveSide, boolean vertical) {
      ArrayList var5 = new ArrayList();
      if (vertical) {
         if (diff < -0.5) {
            var5.add(negativeSide);
         }

         if (AntiCheat.INSTANCE.upDirectionLimit.getValue()) {
            if (diff > 0.5) {
               var5.add(positiveSide);
            }
         } else if (diff > -0.5) {
            var5.add(positiveSide);
         }
      } else {
         if (diff < -0.5) {
            var5.add(negativeSide);
         }

         if (diff > 0.5) {
            var5.add(positiveSide);
         }
      }

      return var5;
   }

   public static ArrayList<BlockEntity> getTileEntities() {
      return getLoadedChunks().flatMap(chunk -> chunk.getBlockEntities().values().stream()).collect(Collectors.toCollection(ArrayList::new));
   }

   public static Stream<WorldChunk> getLoadedChunks() {
      int var0 = Math.max(2, mc.options.getClampedViewDistance()) + 3;
      int var1 = var0 * 2 + 1;
      ChunkPos var2 = mc.player.getChunkPos();
      ChunkPos var3 = new ChunkPos(var2.x - var0, var2.z - var0);
      ChunkPos var4 = new ChunkPos(var2.x + var0, var2.z + var0);
      return Stream.<ChunkPos>iterate(var3, pos -> {
         int var3x = pos.x;
         int var4x = pos.z;
         if (++var3x > var4.x) {
            var3x = var3.x;
            var4x++;
         }

         return new ChunkPos(var3x, var4x);
      }).limit((long)var1 * (long)var1).filter(c -> mc.world.isChunkLoaded(c.x, c.z)).map(c -> mc.world.getChunk(c.x, c.z)).filter(Objects::nonNull);
   }

   public static ArrayList<BlockPos> getSphere(float range) {
      return getSphere(range, mc.player.getEyePos());
   }

   public static BlockPos getBlock(Block block, float range) {
      for (BlockPos var3 : getSphere(range)) {
         if (mc.world.getBlockState(var3).getBlock() == block) {
            return var3;
         }
      }

      return null;
   }

   public static BlockPos getBlock(Class<?> block, float range) {
      for (BlockPos var3 : getSphere(range)) {
         if (block.isInstance(mc.world.getBlockState(var3).getBlock())) {
            return var3;
         }
      }

      return null;
   }

   public static ArrayList<BlockPos> getSphere(float range, Vec3d pos) {
      ArrayList var2 = new ArrayList();

      for (double var3 = pos.getY() + (double)range; var3 > pos.getY() - (double)range; var3--) {
         if (!(var3 < -64.0)) {
            for (double var5 = pos.getX() - (double)range; var5 < pos.getX() + (double)range; var5++) {
               for (double var7 = pos.getZ() - (double)range; var7 < pos.getZ() + (double)range; var7++) {
                  BlockPosX var9 = new BlockPosX(var5, var3, var7);
                  if (!(var9.toCenterPos().distanceTo(pos) > (double)range)) {
                     var2.add(var9);
                  }
               }
            }
         }
      }

      return var2;
   }

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

   public static boolean canReplace(BlockPos pos) {
      if (pos.getY() >= 320) {
         return false;
      } else if (AntiCheat.INSTANCE.multiPlace.getValue() && placedPos.contains(pos)) {
         return false;
      } else {
         BlockState var1 = mc.world.getBlockState(pos);
         return var1.getBlock() == Blocks.COBWEB && AutoWeb.ignore && AutoCrystal.INSTANCE.replace.getValue() ? true : var1.isReplaceable();
      }
   }

   public static boolean canClick(BlockPos pos) {
      if (AntiCheat.INSTANCE.multiPlace.getValue() && placedPos.contains(pos)) {
         return true;
      } else {
         BlockState var1 = mc.world.getBlockState(pos);
         Block var2 = var1.getBlock();
         return var2 == Blocks.COBWEB && AutoWeb.ignore ? AutoCrystal.INSTANCE.airPlace.getValue() : mc.player.isSneaking() || !isClickable(var2);
      }
   }

   public static boolean isClickable(Block block) {
      return block instanceof CraftingTableBlock
         || block instanceof AnvilBlock
         || block instanceof LoomBlock
         || block instanceof CartographyTableBlock
         || block instanceof GrindstoneBlock
         || block instanceof StonecutterBlock
         || block instanceof ButtonBlock
         || block instanceof AbstractPressurePlateBlock
         || block instanceof BlockWithEntity
         || block instanceof BedBlock
         || block instanceof FenceGateBlock
         || block instanceof DoorBlock
         || block instanceof NoteBlock
         || block instanceof TrapdoorBlock;
   }

   public static boolean canCollide(Box box) {
      return canCollide(mc.player, box);
   }

   public static boolean canCollide(@Nullable Entity entity, Box box) {
      BlockCollisionSpliterator var2 = new BlockCollisionSpliterator(mc.world, entity, box, false, (pos, voxelShape) -> voxelShape);

      while (var2.hasNext()) {
         if (!((VoxelShape)var2.next()).isEmpty()) {
            return true;
         }
      }

      return false;
   }

   public static boolean allowAirPlace() {
      return AirPlace.INSTANCE.isOn() && AirPlace.INSTANCE.module.getValue();
   }
}
