package dev.crucifixbeta.api.utils.rotation;

import dev.crucifixbeta.CrucifixBeta;
import dev.crucifixbeta.core.impl.RotationManager;
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.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.MathHelper;
import net.minecraft.util.math.Vec3d;

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

   public static Rotation calculate(Vec3d from, Vec3d to) {
      Vec3d var2 = to.subtract(from);
      double var3 = Math.hypot(var2.x, var2.z);
      float var5 = (float)(Math.atan2(var2.z, var2.x) * 180.0F / (float)Math.PI) - 90.0F;
      float var6 = (float)(-(Math.atan2(var2.y, var3) * 180.0F / (float)Math.PI));
      return new Rotation(var5, var6);
   }

   public static Rotation calculate(Entity entity) {
      if (mc.player == null) {
         return new Rotation(0.0F, 0.0F);
      } else {
         double var1 = (double)mc.player.getEyeHeight(mc.player.getPose());
         double var3 = Math.max(0.0, Math.min(mc.player.getY() - entity.getY() + var1, (entity.getBoundingBox().maxY - entity.getBoundingBox().minY) * 0.9));
         return calculate(entity.getPos().add(0.0, var3, 0.0));
      }
   }

   public static Rotation calculate(Entity entity, boolean adaptive, double range) {
      Rotation var4 = calculate(entity);
      HitResult var5 = RayCastUtil.rayCast(var4, range, 0.0F);
      if (adaptive && (var5 == null || var5.getType() != Type.ENTITY)) {
         Box var6 = entity.getBoundingBox();
         double var7 = var6.minX;
         double var9 = var6.maxX;
         double var11 = var6.minY;
         double var13 = var6.maxY;
         double var15 = var6.minZ;
         double var17 = var6.maxZ;
         Vec3d var19 = entity.getPos();

         for (double var20 = 1.0; var20 >= 0.0; var20 -= 0.25 + Math.random() * 0.1) {
            for (double var22 = 1.0; var22 >= -0.5; var22 -= 0.5) {
               for (double var24 = 1.0; var24 >= -0.5; var24 -= 0.5) {
                  double var26 = (var9 - var7) * var22;
                  double var28 = (var13 - var11) * var20;
                  double var30 = (var17 - var15) * var24;
                  Vec3d var32 = var19.add(var26, var28, var30);
                  Rotation var33 = calculate(var32);
                  HitResult var34 = RayCastUtil.rayCast(var33, range, 0.0F);
                  if (var34 != null && var34.getType() == Type.ENTITY) {
                     return var33;
                  }
               }
            }
         }

         return var4;
      } else {
         return var4;
      }
   }

   public static Rotation calculate(BlockPos to) {
      return calculate(mc.player.getEyePos(), new Vec3d((double)to.getX(), (double)to.getY(), (double)to.getZ()).add(0.5, 0.5, 0.5));
   }

   public static Rotation calculate(Vec3d to) {
      return calculate(mc.player.getEyePos(), to);
   }

   public static Rotation calculate(Vec3d position, Direction enumFacing) {
      double var2 = position.x + 0.5;
      double var4 = position.y + 0.5;
      double var6 = position.z + 0.5;
      var2 += (double)enumFacing.getVector().getX() * 0.5;
      var4 += (double)enumFacing.getVector().getY() * 0.5;
      var6 += (double)enumFacing.getVector().getZ() * 0.5;
      return calculate(new Vec3d(var2, var4, var6));
   }

   public static Rotation calculate(BlockPos position, Direction enumFacing) {
      double var2 = (double)position.getX() + 0.5;
      double var4 = (double)position.getY() + 0.5;
      double var6 = (double)position.getZ() + 0.5;
      var2 += (double)enumFacing.getVector().getX() * 0.5;
      var4 += (double)enumFacing.getVector().getY() * 0.5;
      var6 += (double)enumFacing.getVector().getZ() * 0.5;
      return calculate(new Vec3d(var2, var4, var6));
   }

   public static Rotation applySensitivityPatch(Rotation rotation) {
      Rotation var1 = RotationManager.lastRotations;
      if (var1 == null) {
         var1 = new Rotation(mc.player.prevYaw, mc.player.prevPitch);
      }

      return applySensitivityPatch(rotation, var1);
   }

   public static Rotation applySensitivityPatch(Rotation rotation, Rotation previousRotation) {
      float var2 = (float)(mc.options.getMouseSensitivity().getValue() * 0.6F + 0.2F);
      double var3 = (double)(var2 * var2 * var2 * 8.0F) * 0.15;
      float var5 = previousRotation.getYaw() + (float)((double)Math.round((double)(rotation.getYaw() - previousRotation.getYaw()) / var3) * var3);
      float var6 = previousRotation.getPitch() + (float)((double)Math.round((double)(rotation.getPitch() - previousRotation.getPitch()) / var3) * var3);
      return new Rotation(var5, MathHelper.clamp(var6, -90.0F, 90.0F));
   }

   public static Rotation relateToPlayerRotation(Rotation rotation) {
      Rotation var1 = new Rotation(mc.player.prevYaw, mc.player.prevPitch);
      float var2 = var1.getYaw() + MathHelper.wrapDegrees(rotation.getYaw() - var1.getYaw());
      float var3 = MathHelper.clamp(rotation.getPitch(), -90.0F, 90.0F);
      return new Rotation(var2, var3);
   }

   public static Rotation resetRotation(Rotation rotation) {
      if (rotation == null) {
         return null;
      } else {
         float var1 = rotation.getYaw() + MathHelper.wrapDegrees(mc.player.getYaw() - rotation.getYaw());
         float var2 = mc.player.getPitch();
         return new Rotation(var1, var2);
      }
   }

   public static Rotation move(Rotation targetRotation, double speed) {
      return move(RotationManager.lastRotations, targetRotation, speed);
   }

   public static Rotation move(Rotation lastRotation, Rotation targetRotation, double speed) {
      if (speed != 0.0) {
         double var4 = (double)MathHelper.wrapDegrees(targetRotation.getYaw() - lastRotation.getYaw());
         double var6 = (double)(targetRotation.getPitch() - lastRotation.getPitch());
         double var8 = Math.sqrt(var4 * var4 + var6 * var6);
         double var10 = Math.abs(var4 / var8);
         double var12 = Math.abs(var6 / var8);
         double var14 = speed * var10;
         double var16 = speed * var12;
         float var18 = (float)Math.max(Math.min(var4, var14), -var14);
         float var19 = (float)Math.max(Math.min(var6, var16), -var16);
         return new Rotation(var18, var19);
      } else {
         return new Rotation(0.0F, 0.0F);
      }
   }

   public static Rotation smooth(Rotation targetRotation, double speed) {
      return smooth(RotationManager.lastRotations, targetRotation, speed);
   }

   public static Rotation smooth(Rotation lastRotation, Rotation targetRotation, double speed) {
      float var4 = targetRotation.getYaw();
      float var5 = targetRotation.getPitch();
      float var6 = lastRotation.getYaw();
      float var7 = lastRotation.getPitch();
      if (speed != 0.0) {
         Rotation var8 = move(targetRotation, speed);
         var4 = var6 + var8.getYaw();
         var5 = var7 + var8.getPitch();

         for (int var9 = 1; var9 <= (int)((float)CrucifixBeta.FPS.getFps() / 20.0F + (float)Math.random() * 10.0F); var9++) {
            if ((double)(Math.abs(var8.getYaw()) + Math.abs(var8.getPitch())) > 1.0E-4) {
               var4 = (float)((double)var4 + (Math.random() - 0.5) / 1000.0);
               var5 = (float)((double)var5 - Math.random() / 200.0);
            }

            Rotation var10 = new Rotation(var4, var5);
            Rotation var11 = applySensitivityPatch(var10, lastRotation);
            var4 = shortestYaw(var6, var11.getYaw());
            var5 = Math.max(-90.0F, Math.min(90.0F, var11.getPitch()));
         }
      }

      return new Rotation(var4, var5);
   }

   public static double getDistance(Entity entity) {
      Vec3d var1 = mc.player.getEyePos();
      Box var2 = entity.getBoundingBox();
      double var3 = MathHelper.clamp(var1.x, var2.minX, var2.maxX);
      double var5 = MathHelper.clamp(var1.y, var2.minY, var2.maxY);
      double var7 = MathHelper.clamp(var1.z, var2.minZ, var2.maxZ);
      return Math.sqrt(var1.squaredDistanceTo(var3, var5, var7));
   }

   public static double getDistance(PlayerEntity player, Entity entity) {
      Vec3d var2 = player.getEyePos();
      Box var3 = entity.getBoundingBox();
      double var4 = MathHelper.clamp(var2.x, var3.minX, var3.maxX);
      double var6 = MathHelper.clamp(var2.y, var3.minY, var3.maxY);
      double var8 = MathHelper.clamp(var2.z, var3.minZ, var3.maxZ);
      return Math.sqrt(var2.squaredDistanceTo(var4, var6, var8));
   }

   public static float shortestYaw(float from, float to) {
      return from + MathHelper.wrapDegrees(to - from);
   }
}
