content: package pers.neige.neigeitems.action;
  
  import lombok.NonNull;
  import lombok.val;
  import org.bukkit.Bukkit;
  import org.jetbrains.annotations.Nullable;
  import pers.neige.neigeitems.manager.BaseActionManager;
  import pers.neige.neigeitems.utils.SchedulerUtils;
  import pers.neige.neigeitems.utils.lazy.ThreadSafeLazyBoolean;
  
  import java.util.Arrays;
  import java.util.concurrent.CompletableFuture;
  
  public abstract class Action {
      protected final @NonNull BaseActionManager manager;
      protected @Nullable ThreadSafeLazyBoolean canRunInOtherThread = null;
  
      public Action(@NonNull BaseActionManager manager) {
          this.manager = manager;
      }
  
      public @NonNull ActionType getType() {
          return ActionType.UNKNOWN;
      }
  
      protected abstract @NonNull CompletableFuture<ActionResult> eval(
              @NonNull BaseActionManager manager,
              @NonNull ActionContext context
      );
  
      public @NonNull CompletableFuture<ActionResult> evalAsyncSafe(
              @NonNull BaseActionManager manager,
              @NonNull ActionContext context
      ) {
          if (canRunInOtherThread()) {
              // 如果线程状态不一致, 回归原始线程
              if (context.isSync() != Bukkit.isPrimaryThread()) {
                  val result = new CompletableFuture<ActionResult>();
                  SchedulerUtils.run(manager.getPlugin(), context.isSync(), () -> {
                      eval(manager, context).thenAccept(result::complete);
                  });
                  return result;
              }
              // 非主线程运行非线程安全动作, 进行线程切换
          } else {
              if (!Bukkit.isPrimaryThread()) {
                  val result = new CompletableFuture<ActionResult>();
                  // 转主线程
                  Bukkit.getScheduler().runTask(manager.getPlugin(), () -> {
                      eval(manager, context).thenAccept(result::complete);
                  });
                  return result;
              }
          }
          return eval(manager, context);
      }
  
      /**
       * 当前动作是否可以在非主线程运行.<br>
       * 假设当前动作是单一动作, 比如 String 类型动作.<br>
       * 那么此方法应反映当前动作的运行逻辑.<br>
       * 假设当前动作是多个动作的组合, 比如 List 类型动作.<br>
       * 那么, 只有组合内所有动作均需要在主线程运行时, 当前动作组合才需要在主线程运行.<br>
       * 或者说, 只要组合内存在不需要在主线程运行的动作, 就认定该动作组合不需要在主线程运行.<br>
       * 具体原因与线程切换判断有关, 是为了防止混合动作执行过程中频繁地反复横跳.
       */
      public boolean canRunInOtherThread() {
          return canRunInOtherThread == null || canRunInOtherThread.get();
      }
  
      public @NonNull CompletableFuture<ActionResult> evalAsyncSafe(
              @NonNull ActionContext context
      ) {
          return evalAsyncSafe(manager, context);
      }
  
      public @NonNull CompletableFuture<ActionResult> eval(
              @NonNull ActionContext context
      ) {
          return evalAsyncSafe(manager, context.clone());
      }
  
      /**
       * 向当前动作前插入一个动作, 并返回合并后的新动作.
       *
       * @param action 插入的动作
       * @return 新动作
       */
      public @NonNull Action insertBefore(@Nullable Object action) {
          return manager.compile(Arrays.asList(action, this));
      }
  
      /**
       * 向当前动作后插入一个动作, 并返回合并后的新动作.
       *
       * @param action 插入的动作
       * @return 新动作
       */
      public @NonNull Action insertAfter(@Nullable Object action) {
          return manager.compile(Arrays.asList(this, action));
      }
  }
  