package dev.crucifixbeta.api.utils.math;

import asm.accessors.IEntity;
import dev.crucifixbeta.api.utils.Wrapper;
import dev.crucifixbeta.api.utils.world.BlockPosX;
import dev.crucifixbeta.api.utils.world.BlockUtil;
import dev.crucifixbeta.mod.modules.impl.client.AntiCheat;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Vec3d;

@Environment(EnvType.CLIENT)
public class PredictUtil implements Wrapper {
   public static Vec3d getPos(PlayerEntity entity, int ticks) {
      return ticks <= 0
         ? entity.getPos()
         : getPos(
            entity,
            AntiCheat.INSTANCE.maxMotionY.getValue(),
            AntiCheat.INSTANCE.predictTicks.getValueInt(),
            AntiCheat.INSTANCE.simulation.getValueInt(),
            AntiCheat.INSTANCE.step.getValue(),
            AntiCheat.INSTANCE.doubleStep.getValue(),
            AntiCheat.INSTANCE.jump.getValue(),
            AntiCheat.INSTANCE.inBlockPause.getValue()
         );
   }

   public static Vec3d getPos(
      PlayerEntity e, double maxMotionY, int ticks, int simulation, boolean step, boolean doubleStep, boolean jump, boolean inBlockPause
   ) {
      if (inBlockPause && BlockUtil.canCollide(e, e.getBoundingBox())) {
         return e.getPos();
      } else {
         double var9;
         double var11;
         double var13;
         if (AntiCheat.INSTANCE.motion.is(AntiCheat.Motion.Position)) {
            var9 = e.getX() - e.prevX;
            var11 = e.getY() - e.prevY;
            var13 = e.getZ() - e.prevZ;
            if (var11 > maxMotionY) {
               var11 = maxMotionY;
            }
         } else {
            var9 = e.getVelocity().x;
            var11 = e.getVelocity().y;
            var13 = e.getVelocity().z;
         }

         double var15 = var9;
         double var17 = var11;
         double var19 = var13;
         double var21 = e.getX();
         double var23 = e.getY();
         double var25 = e.getZ();
         Vec3d var27 = new Vec3d(var21, var23, var25);
         if (var9 == 0.0 && var11 == 0.0 && var13 == 0.0) {
            return var27;
         } else {
            for (int var28 = 0; var28 < ticks; var28++) {
               var27 = new Vec3d(var21, var23, var25);
               boolean var29 = false;
               boolean var30 = false;
               int var31 = simulation;

               label146:
               while (true) {
                  if (var31 >= 0) {
                     int var32 = simulation;

                     label141:
                     while (true) {
                        if (var32 < 0) {
                           var31--;
                           continue label146;
                        }

                        double var33 = (double)var32 / (double)simulation;
                        double var35 = (double)var31 / (double)simulation;
                        if (canMove(var27.add(var15 * var33, var17 * var35, var19 * var33), e)) {
                           if (Math.abs(var15 * var33) + Math.abs(var19 * var33) + Math.abs(var17 * var35) <= 0.05) {
                              if (step && !canMove(var27.add(var9, 0.0, var13), e) && canMove(var27.add(var9, 1.1, var13), e)) {
                                 var23++;
                                 var17 = 0.03;

                                 for (int var44 = simulation; var44 >= 0; var44--) {
                                    for (int var45 = simulation; var45 >= 0; var45--) {
                                       double var46 = (double)var45 / (double)simulation;
                                       double var47 = (double)var44 / (double)simulation;
                                       if (canMove(var27.add(var15 * var46, var17 * var47, var19 * var46), e)) {
                                          var29 = true;
                                          var21 += var15 * var46;
                                          var25 += var19 * var46;
                                          if (var44 > 0) {
                                             var23 += var17 * var47;
                                             var30 = true;
                                          }
                                          break label141;
                                       }
                                    }
                                 }

                                 return var27;
                              } else {
                                 if (!doubleStep || canMove(var27.add(var9, 0.0, var13), e) || !canMove(var27.add(var9, 2.1, var13), e)) {
                                    return var27;
                                 }

                                 var23 += 2.05;
                                 var17 = 0.03;

                                 for (int var37 = simulation; var37 >= 0; var37--) {
                                    for (int var38 = simulation; var38 >= 0; var38--) {
                                       double var39 = (double)var38 / (double)simulation;
                                       double var41 = (double)var37 / (double)simulation;
                                       if (canMove(var27.add(var15 * var39, var17 * var41, var19 * var39), e)) {
                                          var29 = true;
                                          var21 += var15 * var39;
                                          var25 += var19 * var39;
                                          if (var37 > 0) {
                                             var23 += var17 * var41;
                                             var30 = true;
                                          }
                                          break label141;
                                       }
                                    }
                                 }

                                 return var27;
                              }
                           } else {
                              var29 = true;
                              var21 += var15 * var33;
                              var25 += var19 * var33;
                              if (var31 > 0) {
                                 var23 += var17 * var35;
                                 var30 = true;
                              }
                              break;
                           }
                        }

                        var32--;
                     }
                  }

                  if (!var29) {
                     return var27;
                  }

                  if (!e.isFallFlying()) {
                     var15 *= 0.99;
                     var19 *= 0.99;
                     var17 *= 0.99;
                     var17 -= 0.05F;
                  }

                  if (!var30) {
                     if (e.isOnGround()) {
                        var15 = var9;
                        var19 = var13;
                        var17 = 0.0;
                     } else if (jump) {
                        var15 = var9;
                        var19 = var13;
                        var17 = 0.333;
                     } else {
                        var17 = 0.0;
                     }
                  }
                  break;
               }
            }

            return var27;
         }
      }
   }

   public static boolean canMove(Vec3d pos, PlayerEntity player) {
      return !BlockUtil.canCollide(player, ((IEntity)player).getDimensions().getBoxAt(pos)) || new Box(new BlockPosX(pos)).intersects(player.getBoundingBox());
   }
}
