package dev.crucifixbeta.api.utils.rotation;

import dev.crucifixbeta.core.impl.RotationManager;
import java.util.Optional;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.hit.HitResult.Type;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.RaycastContext;
import net.minecraft.world.RaycastContext.FluidHandling;
import net.minecraft.world.RaycastContext.ShapeType;

@Environment(EnvType.CLIENT)
public final class RayCastUtil {
   private static final MinecraftClient mc = MinecraftClient.getInstance();

   public static HitResult rayCast(Rotation rotation, double range) {
      return rayCast(rotation, range, 0.0F);
   }

   public static boolean inView(Entity entity) {
      if (mc.player != null && mc.world != null && entity != null) {
         double var1 = (double)mc.player.distanceTo(entity);
         double var3 = Math.min(256.0, var1 + 2.0);
         Rotation var5 = RotationUtils.calculate(entity);
         if (!(var1 > 100.0) && entity instanceof PlayerEntity) {
            Box var23 = entity.getBoundingBox();
            double var7 = var23.getLengthX();
            double var9 = var23.getLengthY();
            double var11 = var23.getLengthZ();
            Vec3d var13 = entity.getPos();

            for (double var14 = 1.0; var14 >= -1.0; var14 -= 0.5) {
               for (double var16 = 1.0; var16 >= -1.0; var16--) {
                  for (double var18 = 1.0; var18 >= -1.0; var18--) {
                     Vec3d var20 = var13.add(var7 * var16, var9 * var14, var11 * var18);
                     Rotation var21 = RotationUtils.calculate(var20);
                     HitResult var22 = rayCast(var21, var3, 0.2F);
                     if (var22 != null && var22.getType() == Type.ENTITY) {
                        return true;
                     }
                  }
               }
            }

            return false;
         } else {
            HitResult var6 = rayCast(var5, var3, 0.2F);
            return var6 != null && var6.getType() == Type.ENTITY;
         }
      } else {
         return false;
      }
   }

   public static HitResult rayCast(Rotation rotation, double range, float expand) {
      return rayCast(rotation, range, expand, mc.player);
   }

   public static HitResult rayCast(Rotation rotation, double range, float expand, Entity entity) {
      if (mc.world != null && entity != null) {
         Vec3d var5 = entity.getEyePos();
         Vec3d var6 = Vec3d.fromPolar(rotation.getPitch(), rotation.getYaw());
         Vec3d var7 = var5.add(var6.multiply(range));
         BlockHitResult var8 = mc.world.raycast(new RaycastContext(var5, var7, ShapeType.OUTLINE, FluidHandling.NONE, entity));
         double var9 = range;
         if (var8 != null && var8.getType() != Type.MISS) {
            var9 = var8.getPos().distanceTo(var5);
         }

         Vec3d var11 = var7;
         Entity var12 = null;
         Vec3d var13 = null;
         double var14 = var9;
         Box var16 = entity.getBoundingBox().stretch(var6.multiply(range)).expand(1.0);

         for (Entity var19 : mc.world.getOtherEntities(entity, var16, e -> !e.isSpectator() && e.canHit())) {
            double var20 = (double)(var19.getTargetingMargin() + expand);
            Box var22 = var19.getBoundingBox().expand(var20);
            Optional<Vec3d> var23 = var22.raycast(var5, var11);
            if (var22.contains(var5)) {
               if (var14 >= 0.0) {
                  var12 = var19;
                  var13 = var23.orElse(var5);
                  var14 = 0.0;
               }
            } else if (var23.isPresent()) {
               Vec3d var24 = (Vec3d)var23.get();
               double var25 = var5.distanceTo(var24);
               if ((var25 < var14 || var14 == 0.0) && (var19.getRootVehicle() != entity.getRootVehicle() || var14 == 0.0)) {
                  var12 = var19;
                  var13 = var24;
                  var14 = var25;
               }
            }
         }

         return (HitResult)(var12 == null || !(var14 < var9) && var8 != null && var8.getType() != Type.MISS ? var8 : new EntityHitResult(var12, var13));
      } else {
         return null;
      }
   }

   public static boolean overBlock(Rotation rotation, Direction enumFacing, BlockPos pos, boolean strict) {
      if (mc.player != null && mc.world != null) {
         Vec3d var4 = Vec3d.fromPolar(rotation.getPitch(), rotation.getYaw());
         Vec3d var5 = mc.player.getEyePos();
         double var6 = 4.5;
         Vec3d var8 = var5.add(var4.multiply(var6));
         BlockHitResult var9 = mc.world.raycast(new RaycastContext(var5, var8, ShapeType.OUTLINE, FluidHandling.NONE, mc.player));
         return var9.getType() == Type.MISS ? false : var9.getBlockPos().equals(pos) && (!strict || var9.getSide() == enumFacing);
      } else {
         return false;
      }
   }

   public static boolean overBlock(Direction enumFacing, BlockPos pos, boolean strict) {
      return overBlock(RotationManager.getRotation(), enumFacing, pos, strict);
   }

   public static Boolean overBlock(Rotation rotation, BlockPos pos) {
      return overBlock(rotation, Direction.UP, pos, false);
   }

   public static Boolean overBlock(Rotation rotation, BlockPos pos, Direction enumFacing) {
      return overBlock(rotation, enumFacing, pos, true);
   }
}
