content: package pers.neige.neigeitems.libs.bot.inker.bukkit.nbt.neigeitems.utils;
  
  import lombok.NonNull;
  import net.md_5.bungee.api.chat.BaseComponent;
  import net.md_5.bungee.chat.ComponentSerializer;
  import org.bukkit.Location;
  import org.bukkit.entity.Entity;
  import org.bukkit.entity.FishHook;
  import org.bukkit.entity.LivingEntity;
  import org.bukkit.util.NumberConversions;
  import org.jetbrains.annotations.Nullable;
  import pers.neige.neigeitems.libs.bot.inker.bukkit.nbt.NbtCompound;
  import pers.neige.neigeitems.libs.bot.inker.bukkit.nbt.NeigeItemsUtils;
  import pers.neige.neigeitems.libs.bot.inker.bukkit.nbt.internal.annotation.CbVersion;
  import pers.neige.neigeitems.ref.argument.RefAnchor;
  import pers.neige.neigeitems.ref.chat.RefChatSerializer;
  import pers.neige.neigeitems.ref.chat.RefComponent;
  import pers.neige.neigeitems.ref.entity.*;
  import pers.neige.neigeitems.ref.nbt.RefNbtTagCompound;
  import pers.neige.neigeitems.ref.server.level.RefTrackedEntity;
  import pers.neige.neigeitems.ref.world.RefVec3;
  
  import java.util.Random;
  
  public class EntityUtils {
      /**
       * 1.14+ 版本起, Entity#move 方法需传入一个 Vec3 实例.
       * 在此前的版本, Entity#move 方法需传入三个 double, 用于确定方向.
       */
      private static final boolean VEC3_MOVE_SUPPORT = CbVersion.v1_14_R1.isSupport();
  
      /**
       * 1.17+ 版本起, Entity 内部的 yaw/pitch 字段设置为私有, 需要通过方法进行操作.
       */
      private static final boolean GET_YAW_SUPPORT = CbVersion.v1_17_R1.isSupport();
  
      /**
       * 1.14+ 版本起, Entity 接口添加 setRotation 方法, 用于设置实体朝向.
       */
      private static final boolean SET_ROTATION_SUPPORT = CbVersion.v1_14_R1.isSupport();
  
      /**
       * 1.17+ 版本起, Entity 内部的添加 lookAt 方法, 用于使实体看向指定位置.
       */
      private static final boolean LOOK_AT_SUPPORT = CbVersion.v1_17_R1.isSupport();
      /**
       * 1.13+ 版本起, Entity 类 setCustomName 方法由接收 String 改为接收 IChatBaseComponent.
       */
      private static final boolean COMPONENT_NAME_SUPPORT = CbVersion.v1_13_R1.isSupport();
  
      /**
       * 将实体信息保存至 NBT.
       *
       * @param entity 待保存实体.
       * @return 包含实体信息的 NBT.
       */
      public static @Nullable NbtCompound save(
              @NonNull Entity entity
      ) {
          if (entity instanceof RefCraftEntity) {
              RefNbtTagCompound nbt = new RefNbtTagCompound();
              ((RefCraftEntity) entity).getHandle().save(nbt);
              return NeigeItemsUtils.fromNms(nbt);
          }
          return null;
      }
  
      /**
       * 令实体加载 NBT 信息.
       *
       * @param entity 待操作实体.
       * @param nbt    待加载 NBT.
       */
      public static void load(
              @NonNull Entity entity,
              @NonNull NbtCompound nbt
      ) {
          if (entity instanceof RefCraftEntity) {
              ((RefCraftEntity) entity).getHandle().load(NeigeItemsUtils.toNms(nbt));
          }
      }
  
      /**
       * 让实体尝试寻路并移动到对应位置.
       *
       * @param entity 待操作实体.
       * @param x      目标点 x 轴坐标.
       * @param y      目标点 y 轴坐标.
       * @param z      目标点 z 轴坐标.
       * @param speed  移动速度.
       * @return 寻路是否成功.
       */
      public static boolean tryToMoveTo(
              @NonNull LivingEntity entity,
              double x,
              double y,
              double z,
              double speed
      ) {
          if (entity instanceof RefCraftLivingEntity) {
              RefEntityLiving living = ((RefCraftLivingEntity) entity).getHandle();
              if (living instanceof RefMob) {
                  return ((RefMob) living).getNavigation().moveTo(x, y, z, speed);
              }
          }
          return false;
      }
  
      /**
       * 让实体尝试寻路并移动到对应实体位置.
       *
       * @param entity 待操作实体.
       * @param target 目标实体.
       * @param speed  移动速度.
       * @return 寻路是否成功.
       */
      public static boolean tryToMoveTo(
              @NonNull LivingEntity entity,
              Entity target,
              double speed
      ) {
          if (entity instanceof RefCraftLivingEntity && target instanceof RefCraftEntity) {
              RefEntityLiving living = ((RefCraftLivingEntity) entity).getHandle();
              if (living instanceof RefMob) {
                  return ((RefMob) living).getNavigation().moveTo(((RefCraftEntity) target).getHandle(), speed);
              }
          }
          return false;
      }
  
      /**
       * 尝试tick实体.
       *
       * @param entity 待操作实体.
       */
      public static void tick(@NonNull Entity entity) {
          if (entity instanceof RefCraftEntity) {
              RefEntity nmsEntity = ((RefCraftEntity) entity).getHandle();
              nmsEntity.tick();
          }
      }
  
      /**
       * 尝试tick实体寻路.
       *
       * @param entity 待操作实体.
       */
      public static void tickNavigation(@NonNull LivingEntity entity) {
          if (entity instanceof RefCraftLivingEntity) {
              RefEntityLiving living = ((RefCraftLivingEntity) entity).getHandle();
              if (living instanceof RefMob) {
                  RefMob mob = (RefMob) living;
                  mob.getNavigation().tick();
                  mob.getLookControl().tick();
                  mob.getMoveControl().tick();
                  mob.getJumpControl().tick();
                  living.travel(living.xxa, living.yya, living.zza);
  
                  double d0 = living.locX - living.lastX;
                  double d1 = living.locZ - living.lastZ;
                  float f = (float) ((d0 * d0) + (d1 * d1));
                  float f1 = living.yBodyRot;
                  float f2 = 0.0f;
                  living.oRun = living.run;
                  float f3 = 0.0f;
                  if (f > 0.0025000002f) {
                      f3 = 1.0f;
                      f2 = ((float) Math.sqrt(f)) * 3.0f;
                      float f4 = (((float) MathUtils.c(d1, d0)) * 57.295776f) - 90.0f;
                      float f5 = MathUtils.e(MathUtils.g(living.yaw) - f4);
                      if (95.0f < f5 && f5 < 265.0f) {
                          f1 = f4 - 180.0f;
                      } else {
                          f1 = f4;
                      }
                  }
                  if (living.attackAnim > 0.0f) {
                      f1 = living.yaw;
                  }
                  if (!entity.isOnGround()) {
                      f3 = 0.0f;
                  }
                  living.run += (f3 - living.run) * 0.3f;
                  float f22 = living.tickHeadTurn(f1, f2);
                  while (living.yaw - living.lastYaw < -180.0f) {
                      living.lastYaw -= 360.0f;
                  }
                  while (living.yaw - living.lastYaw >= 180.0f) {
                      living.lastYaw += 360.0f;
                  }
                  while (living.yBodyRot - living.yBodyRotO < -180.0f) {
                      living.yBodyRotO -= 360.0f;
                  }
                  while (living.yBodyRot - living.yBodyRotO >= 180.0f) {
                      living.yBodyRotO += 360.0f;
                  }
                  while (living.pitch - living.lastPitch < -180.0f) {
                      living.lastPitch -= 360.0f;
                  }
                  while (living.pitch - living.lastPitch >= 180.0f) {
                      living.lastPitch += 360.0f;
                  }
                  while (living.yHeadRot - living.yHeadRotO < -180.0f) {
                      living.yHeadRotO -= 360.0f;
                  }
                  while (living.yHeadRot - living.yHeadRotO >= 180.0f) {
                      living.yHeadRotO += 360.0f;
                  }
                  living.animStep += f22;
                  if (living.isFallFlying()) {
                      living.fallFlyTicks++;
                  } else {
                      living.fallFlyTicks = 0;
                  }
              }
          }
      }
  
      /**
       * 强制实体移动.
       *
       * @param entity 待操作实体.
       * @param x      移动方向 x 轴分量.
       * @param y      移动方向 y 轴分量.
       * @param z      移动方向 z 轴分量.
       */
      public static void move(
              @NonNull Entity entity,
              double x,
              double y,
              double z
      ) {
          if (entity instanceof RefCraftEntity) {
              if (VEC3_MOVE_SUPPORT) {
                  ((RefCraftEntity) entity).getHandle().move(RefMoverType.PLAYER, new RefVec3(x, y, z));
              } else {
                  ((RefCraftEntity) entity).getHandle().move(RefMoverType.PLAYER, x, y, z);
              }
          }
      }
  
      /**
       * 获取实体俯仰角.
       *
       * @param entity 待检测实体.
       * @return 实体俯仰角.
       */
      public static float getPitch(
              @NonNull Entity entity
      ) {
          if (entity instanceof RefCraftEntity) {
              return getPitchByNms(((RefCraftEntity) entity).getHandle());
          }
          return 0;
      }
  
      /**
       * 获取实体偏航角.
       *
       * @param entity 待检测实体.
       * @return 实体偏航角.
       */
      public static float getYaw(
              @NonNull Entity entity
      ) {
          if (entity instanceof RefCraftEntity) {
              return getYawByNms(((RefCraftEntity) entity).getHandle());
          }
          return 0;
      }
  
      /**
       * 设置非玩家实体朝向.
       *
       * @param entity 待设置实体.
       * @param yaw    实体偏航角.
       * @param pitch  实体俯仰角.
       */
      public static void setRotation(
              @NonNull Entity entity,
              float yaw,
              float pitch
      ) {
          if (SET_ROTATION_SUPPORT) {
              entity.setRotation(yaw, pitch);
          } else {
              if (entity instanceof RefCraftEntity) {
                  RefEntity nmsEntity = ((RefCraftEntity) entity).getHandle();
                  NumberConversions.checkFinite(yaw, "yaw not finite");
                  NumberConversions.checkFinite(pitch, "pitch not finite");
                  yaw = normalizeYawByNms(yaw);
                  pitch = normalizePitchByNms(pitch);
                  nmsEntity.yaw = yaw;
                  nmsEntity.pitch = pitch;
                  nmsEntity.lastYaw = yaw;
                  nmsEntity.lastPitch = pitch;
                  nmsEntity.setHeadRotation(yaw);
              }
          }
      }
  
      public static float getHeadRotation(
              @NonNull Entity entity
      ) {
          if (entity instanceof RefCraftEntity) {
              RefEntity nmsEntity = ((RefCraftEntity) entity).getHandle();
              return nmsEntity.getHeadRotation();
          }
          return 0;
      }
  
      public static void setHeadRotation(
              @NonNull Entity entity,
              float headRotation
      ) {
          if (entity instanceof RefCraftEntity) {
              RefEntity nmsEntity = ((RefCraftEntity) entity).getHandle();
              nmsEntity.setHeadRotation(headRotation);
          }
      }
  
      public static void absMoveTo(
              @NonNull Entity entity,
              double x,
              double y,
              double z,
              float yaw,
              float pitch
      ) {
          if (entity instanceof RefCraftEntity) {
              ((RefCraftEntity) entity).getHandle().absMoveTo(x, y, z, yaw, pitch);
          }
      }
  
      public static void moveTo(
              @NonNull Entity entity,
              double x,
              double y,
              double z,
              float yaw,
              float pitch
      ) {
          if (entity instanceof RefCraftEntity) {
              ((RefCraftEntity) entity).getHandle().moveTo(x, y, z, yaw, pitch);
          }
      }
  
      /**
       * 令实体看向指定坐标.
       *
       * @param entity 待操作实体.
       * @param target 目标坐标.
       */
      public static void lookAt(
              @NonNull Entity entity,
              @NonNull Location target
      ) {
          lookAt(entity, target.getX(), target.getY(), target.getZ());
      }
  
      /**
       * 令实体看向指定坐标.
       *
       * @param entity 待操作实体.
       * @param x      目标 x 坐标.
       * @param y      目标 y 坐标.
       * @param z      目标 z 坐标.
       */
      public static void lookAt(
              @NonNull Entity entity,
              double x,
              double y,
              double z
      ) {
          if (entity instanceof RefCraftEntity) {
              if (LOOK_AT_SUPPORT) {
                  ((RefCraftEntity) entity).getHandle().lookAt(RefAnchor.EYES, new RefVec3(x, y, z));
              } else {
                  lookAtByNms(entity, x, y, z);
              }
          }
      }
  
      protected static void lookAtByNms(
              @NonNull Entity entity,
              double x,
              double y,
              double z
      ) {
          RefEntity nmsEntity = ((RefCraftEntity) entity).getHandle();
          Location location = entity.getLocation();
          double d0 = x - location.getX();
          double d1 = y - location.getY() + nmsEntity.getEyeHeight();
          double d2 = z - location.getZ();
          double d3 = Math.sqrt(d0 * d0 + d2 * d2);
          nmsEntity.pitch = wrapDegrees((float) (-(Math.atan2(d1, d3) * 57.2957763671875D)));
          nmsEntity.yaw = wrapDegrees((float) (Math.atan2(d2, d0) * 57.2957763671875D) - 90.0F);
          nmsEntity.setHeadRotation(nmsEntity.yaw);
          nmsEntity.lastPitch = nmsEntity.pitch;
          nmsEntity.lastYaw = nmsEntity.yaw;
      }
  
      /**
       * 令实体看向指定坐标.
       *
       * @param entity 待操作实体.
       * @param target 目标坐标.
       */
      public static void lookAt(
              @NonNull LivingEntity entity,
              @NonNull Location target
      ) {
          lookAt(entity, target.getX(), target.getY(), target.getZ());
      }
  
      /**
       * 令实体看向指定坐标.
       *
       * @param entity 待操作实体.
       * @param x      目标 x 坐标.
       * @param y      目标 y 坐标.
       * @param z      目标 z 坐标.
       */
      public static void lookAt(
              @NonNull LivingEntity entity,
              double x,
              double y,
              double z
      ) {
          if (entity instanceof RefCraftLivingEntity) {
              if (LOOK_AT_SUPPORT) {
                  ((RefCraftLivingEntity) entity).getHandle().lookAt(RefAnchor.EYES, new RefVec3(x, y, z));
              } else {
                  lookAtByNms(entity, x, y, z);
              }
          }
      }
  
      /**
       * 为实体设置自定义名称.
       *
       * @param entity 待操作实体.
       * @param name   自定义名称.
       */
      public static void setCustomName(
              @NonNull Entity entity,
              @NonNull BaseComponent name
      ) {
          if (COMPONENT_NAME_SUPPORT && entity instanceof RefCraftEntity) {
              ((RefCraftEntity) entity).getHandle().setCustomName(toNms(name));
          }
      }
  
      /**
       * 获取实体ID.
       *
       * @param entity 待获取实体.
       * @return 实体ID.
       */
      public static int getId(
              @NonNull Entity entity
      ) {
          if (entity instanceof RefCraftEntity) {
              return ((RefCraftEntity) entity).getHandle().getId();
          }
          return 0;
      }
  
      /**
       * 设置实体隐身状态.
       *
       * @param entity    待操作实体.
       * @param invisible 是否隐身.
       */
      public static void setInvisible(
              @NonNull Entity entity,
              boolean invisible
      ) {
          if (entity instanceof RefCraftEntity) {
              ((RefCraftEntity) entity).getHandle().setInvisible(invisible);
          }
      }
  
      /**
       * 从 TrackedEntity 中获取 Entity.
       *
       * @param trackedEntity 待操作实体.
       */
      public static @NonNull Entity getEntityFromTrackedEntity(
              @NonNull Object trackedEntity
      ) {
          if (!(trackedEntity instanceof RefTrackedEntity)) {
              throw new IllegalArgumentException("trackedEntity must be of type RefTrackedEntity");
          }
          return ((RefTrackedEntity) trackedEntity).entity.getBukkitEntity();
      }
  
      /**
       * 获取等待多久后有鱼游过来(tick).
       *
       * @param hook 待获取鱼钩实体.
       */
      public static int getTimeUntilLured(
              @NonNull FishHook hook
      ) {
          if (!((Object) hook instanceof RefCraftFishHook)) {
              throw new IllegalArgumentException("hook must be of type CraftFishHook");
          }
          return ((RefCraftFishHook) (Object) hook).getHandle().timeUntilLured;
      }
  
      /**
       * 设置等待多久后有鱼游过来(tick).
       *
       * @param hook           待设置鱼钩实体.
       * @param timeUntilLured 等待多久后有鱼游过来(tick).
       */
      public static void setTimeUntilLured(
              @NonNull FishHook hook,
              int timeUntilLured
      ) {
          if (!((Object) hook instanceof RefCraftFishHook)) {
              throw new IllegalArgumentException("hook must be of type CraftFishHook");
          }
          ((RefCraftFishHook) (Object) hook).getHandle().timeUntilLured = timeUntilLured;
      }
  
      /**
       * 获取游多久咬钩(tick).
       *
       * @param hook 待获取鱼钩实体.
       */
      public static int getTimeUntilHooked(
              @NonNull FishHook hook
      ) {
          if (!((Object) hook instanceof RefCraftFishHook)) {
              throw new IllegalArgumentException("hook must be of type CraftFishHook");
          }
          return ((RefCraftFishHook) (Object) hook).getHandle().timeUntilHooked;
      }
  
      /**
       * 设置游多久咬钩(tick).
       *
       * @param hook            待设置鱼钩实体.
       * @param timeUntilHooked 游多久咬钩(tick).
       */
      public static void setTimeUntilHooked(
              @NonNull FishHook hook,
              int timeUntilHooked
      ) {
          if (!((Object) hook instanceof RefCraftFishHook)) {
              throw new IllegalArgumentException("hook must be of type CraftFishHook");
          }
          ((RefCraftFishHook) (Object) hook).getHandle().timeUntilHooked = timeUntilHooked;
      }
  
      /**
       * 获取咬钩多久脱钩(tick).
       *
       * @param hook 待获取鱼钩实体.
       */
      public static int getNibble(
              @NonNull FishHook hook
      ) {
          if (!((Object) hook instanceof RefCraftFishHook)) {
              throw new IllegalArgumentException("hook must be of type CraftFishHook");
          }
          return ((RefCraftFishHook) (Object) hook).getHandle().nibble;
      }
  
      /**
       * 设置咬钩多久脱钩(tick).
       *
       * @param hook   待设置鱼钩实体.
       * @param nibble 咬钩多久脱钩(tick).
       */
      public static void setNibble(
              @NonNull FishHook hook,
              int nibble
      ) {
          if (!((Object) hook instanceof RefCraftFishHook)) {
              throw new IllegalArgumentException("hook must be of type CraftFishHook");
          }
          ((RefCraftFishHook) (Object) hook).getHandle().nibble = nibble;
      }
  
      /**
       * 获取当前鱼钩状态.
       *
       * @param hook 待获取鱼钩实体.
       */
      public static FishHookState getCurrentState(
              @NonNull FishHook hook
      ) {
          if (!((Object) hook instanceof RefCraftFishHook)) {
              throw new IllegalArgumentException("hook must be of type CraftFishHook");
          }
          Enum<?> nmsState = (Enum<?>) ((RefCraftFishHook) (Object) hook).getHandle().currentState;
          return FishHookState.values()[nmsState.ordinal()];
      }
  
      /**
       * 设置当前鱼钩状态.
       *
       * @param hook  待设置鱼钩实体.
       * @param state 鱼钩状态.
       */
      public static void setCurrentState(
              @NonNull FishHook hook,
              @NonNull FishHookState state
      ) {
          if (!((Object) hook instanceof RefCraftFishHook)) {
              throw new IllegalArgumentException("hook must be of type CraftFishHook");
          }
          RefEntityFishingHook nmsHook = ((RefCraftFishHook) (Object) hook).getHandle();
          if (state == FishHookState.FLYING) {
              nmsHook.currentState = RefFishHookState.FLYING;
          } else if (state == FishHookState.HOOKED_IN_ENTITY) {
              nmsHook.currentState = RefFishHookState.HOOKED_IN_ENTITY;
          } else {
              nmsHook.currentState = RefFishHookState.BOBBING;
          }
      }
  
      /**
       * 获取与实体绑定的 Random.
       *
       * @param entity 待获取实体.
       */
      public static @NonNull Random getRandom(
              @NonNull Entity entity
      ) {
          if (!(entity instanceof RefCraftEntity)) {
              throw new IllegalArgumentException("entity must be of type CraftEntity");
          }
          return ((RefCraftEntity) entity).getHandle().random;
      }
  
      protected static RefComponent toNms(BaseComponent component) {
          return RefChatSerializer.fromJson(ComponentSerializer.toString(component));
      }
  
      protected static void lookAtByNms(
              @NonNull LivingEntity entity,
              double x,
              double y,
              double z
      ) {
          RefEntityLiving nmsEntity = ((RefCraftLivingEntity) entity).getHandle();
          lookAtByNms((Entity) entity, x, y, z);
          nmsEntity.yHeadRotO = nmsEntity.yHeadRot;
          nmsEntity.yBodyRot = nmsEntity.yHeadRot;
          nmsEntity.yBodyRotO = nmsEntity.yBodyRot;
      }
  
      private static float wrapDegrees(float degrees) {
          float f = degrees % 360.0F;
          if (f >= 180.0F) {
              f -= 360.0F;
          }
  
          if (f < -180.0F) {
              f += 360.0F;
          }
  
          return f;
      }
  
      private static float normalizeYawByNms(float yaw) {
          yaw %= 360.0F;
          if (yaw >= 180.0F) {
              yaw -= 360.0F;
          } else if (yaw < -180.0F) {
              yaw += 360.0F;
          }
  
          return yaw;
      }
  
      private static float normalizePitchByNms(float pitch) {
          if (pitch > 90.0F) {
              pitch = 90.0F;
          } else if (pitch < -90.0F) {
              pitch = -90.0F;
          }
  
          return pitch;
      }
  
      private static float getPitchByNms(
              @NonNull RefEntity entity
      ) {
          if (GET_YAW_SUPPORT) {
              return entity.getPitch();
          } else {
              return entity.pitch;
          }
      }
  
      private static float getYawByNms(
              @NonNull RefEntity entity
      ) {
          if (GET_YAW_SUPPORT) {
              return entity.getYaw();
          } else {
              return entity.yaw;
          }
      }
  
      public enum FishHookState {
          FLYING,
          HOOKED_IN_ENTITY,
          BOBBING
      }
  }
  