package dev.crucifixbeta.api.utils.math;

import dev.crucifixbeta.api.utils.Wrapper;
import java.math.BigDecimal;
import java.math.RoundingMode;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
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 MathUtil implements Wrapper {
   public static float clamp(float num, float min, float max) {
      return num < min ? min : Math.min(num, max);
   }

   public static double clamp(double value, double min, double max) {
      return value < min ? min : Math.min(value, max);
   }

   public static double round(double value, int places) {
      BigDecimal var3 = new BigDecimal(value);
      var3 = var3.setScale(places, RoundingMode.HALF_UP);
      return var3.doubleValue();
   }

   public static double square(double input) {
      return input * input;
   }

   public static float random(float min, float max) {
      return (float)(Math.random() * (double)(max - min) + (double)min);
   }

   public static double random(double min, double max) {
      return (double)((float)(Math.random() * (max - min) + min));
   }

   public static float rad(float angle) {
      return (float)((double)angle * Math.PI / 180.0);
   }

   public static double interpolate(double previous, double current, double delta) {
      return previous + (current - previous) * delta;
   }

   public static float interpolate(float previous, float current, float delta) {
      return previous + (current - previous) * delta;
   }

   public static Direction getFacingOrder(float yaw, float pitch) {
      float var2 = pitch * (float) (Math.PI / 180.0);
      float var3 = -yaw * (float) (Math.PI / 180.0);
      float var4 = MathHelper.sin(var2);
      float var5 = MathHelper.cos(var2);
      float var6 = MathHelper.sin(var3);
      float var7 = MathHelper.cos(var3);
      boolean var8 = var6 > 0.0F;
      boolean var9 = var4 < 0.0F;
      boolean var10 = var7 > 0.0F;
      float var11 = var8 ? var6 : -var6;
      float var12 = var9 ? -var4 : var4;
      float var13 = var10 ? var7 : -var7;
      float var14 = var11 * var5;
      float var15 = var13 * var5;
      Direction var16 = var8 ? Direction.EAST : Direction.WEST;
      Direction var17 = var9 ? Direction.UP : Direction.DOWN;
      Direction var18 = var10 ? Direction.SOUTH : Direction.NORTH;
      if (var11 > var13) {
         return var12 > var14 ? var17 : var16;
      } else {
         return var12 > var15 ? var17 : var18;
      }
   }

   public static Direction getDirectionFromEntityLiving(BlockPos pos, LivingEntity entity) {
      if (Math.abs(entity.getX() - ((double)pos.getX() + 0.5)) < 2.0 && Math.abs(entity.getZ() - ((double)pos.getZ() + 0.5)) < 2.0) {
         double var2 = entity.getY() + (double)entity.getEyeHeight(entity.getPose());
         if (var2 - (double)pos.getY() > 2.0) {
            return Direction.UP;
         }

         if ((double)pos.getY() - var2 > 0.0) {
            return Direction.DOWN;
         }
      }

      return entity.getHorizontalFacing().getOpposite();
   }

   public static Vec3d getRenderPosition(Entity entity) {
      return getRenderPosition(entity, mc.getRenderTickCounter().getTickDelta(true));
   }

   public static Vec3d getRenderPosition(Entity entity, float tickDelta) {
      return new Vec3d(
         entity.prevX + (entity.getX() - entity.prevX) * (double)tickDelta,
         entity.prevY + (entity.getY() - entity.prevY) * (double)tickDelta,
         entity.prevZ + (entity.getZ() - entity.prevZ) * (double)tickDelta
      );
   }

   public static double getDistance(double x1, double y1, double z1, double x2, double y2, double z2) {
      double var12 = x2 - x1;
      double var14 = y2 - y1;
      double var16 = z2 - z1;
      return Math.sqrt(var12 * var12 + var14 * var14 + var16 * var16);
   }

   public static Vec3d getPointToBoxFromBottom(Vec3d eye, Vec3d bottom, double range, double boxSize, double step) {
      Vec3d var8 = null;
      double var9 = boxSize / 2.0;
      double var11 = Double.MAX_VALUE;
      boolean var13 = bottom.x < eye.x;
      boolean var14 = bottom.z < eye.z;
      double var15 = range * range;
      double var17 = 0.0;

      while (var17 <= boxSize) {
         for (double var19 = 0.0; var19 <= var9; var19 += step) {
            for (double var21 = 0.0; var21 <= var9; var21 += step) {
               double var23 = bottom.y + var17;
               if (var17 != 0.0 && var23 > eye.y) {
                  return var8;
               }

               double var25 = bottom.x + (var13 ? var19 : -var19);
               double var27 = bottom.z + (var14 ? var21 : -var21);
               double var29 = var25 - eye.x;
               double var31 = var23 - eye.y;
               double var33 = var27 - eye.z;
               double var35 = var29 * var29 + var31 * var31 + var33 * var33;
               double var37 = var19 + var17 + var21;
               if (var35 <= var15 && var37 < var11) {
                  var11 = var37;
                  var8 = new Vec3d(var25, var23, var27);
               }
            }
         }

         var17 += step;
      }

      return var8;
   }

   public static Vec3d getClosestPointToBox(Vec3d pos, double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
      double var13 = Math.max(minX, Math.min(pos.x, maxX));
      double var15 = Math.max(minY, Math.min(pos.y, maxY));
      double var17 = Math.max(minZ, Math.min(pos.z, maxZ));
      return new Vec3d(var13, var15, var17);
   }

   public static Vec3d getClosestPointToBox(Vec3d eyePos, Box boundingBox) {
      return getClosestPointToBox(eyePos, boundingBox.minX, boundingBox.minY, boundingBox.minZ, boundingBox.maxX, boundingBox.maxY, boundingBox.maxZ);
   }

   public static Vec3d getClosestPoint(Entity entity) {
      return getClosestPointToBox(mc.player.getEyePos(), entity.getBoundingBox());
   }
}
