content: package pers.neige.neigeitems.action.impl;
  
  import lombok.NonNull;
  import lombok.val;
  import org.jetbrains.annotations.Nullable;
  import pers.neige.neigeitems.action.Action;
  import pers.neige.neigeitems.action.ActionContext;
  import pers.neige.neigeitems.action.ActionResult;
  import pers.neige.neigeitems.action.ActionType;
  import pers.neige.neigeitems.action.evaluator.Evaluator;
  import pers.neige.neigeitems.config.ConfigReader;
  import pers.neige.neigeitems.manager.BaseActionManager;
  import pers.neige.neigeitems.utils.lazy.ThreadSafeLazyBoolean;
  
  import java.util.Locale;
  import java.util.TreeMap;
  import java.util.concurrent.CompletableFuture;
  
  public abstract class TreeAction<T extends Comparable<?>> extends Action {
      protected final @NonNull Class<T> keyType;
      private final @NonNull TreeActionType actionType;
      private final @NonNull String globalId;
      private final @NonNull Action defaultAction;
      private final @NonNull Action matchAction;
      private final @NonNull TreeMap<T, Action> actions = new TreeMap<>();
  
      public TreeAction(
              @NonNull BaseActionManager manager,
              @NonNull ConfigReader config,
              @NonNull Class<T> keyType
      ) {
          super(manager);
          this.keyType = keyType;
          val rawActionType = config.get("action-type");
          TreeActionType actionType;
          try {
              if (rawActionType instanceof String) {
                  actionType = TreeActionType.valueOf(((String) rawActionType).toUpperCase(Locale.ROOT));
              } else if (rawActionType instanceof Integer) {
                  actionType = TreeActionType.values()[(int) rawActionType];
              } else {
                  actionType = TreeActionType.LOWER;
              }
          } catch (Throwable throwable) {
              manager.getPlugin().getLogger().warning(rawActionType + " is not a valid tree action type.");
              actionType = TreeActionType.LOWER;
          }
          this.actionType = actionType;
          this.globalId = config.getString("global-id", "key");
          this.defaultAction = manager.compile(config.get("default-action"));
          this.matchAction = manager.compile(config.get("match-action"));
          val actionsConfig = config.getConfig("actions");
          if (actionsConfig != null) {
              for (val stringKey : actionsConfig.keySet()) {
                  val key = cast(stringKey);
                  if (key == null) {
                      manager.getPlugin().getLogger().warning(stringKey + " is not a valid tree action key.");
                      continue;
                  }
                  this.actions.put(key, manager.compile(actionsConfig.get(stringKey)));
              }
          }
          checkAsyncSafe();
      }
  
      @Override
      public @NonNull ActionType getType() {
          return ActionType.TREE;
      }
  
      public @Nullable T cast(@NonNull Object result) {
          return keyType.isInstance(result) ? keyType.cast(result) : null;
      }
  
      private void checkAsyncSafe() {
          this.canRunInOtherThread = new ThreadSafeLazyBoolean(() -> {
              for (val action : this.actions.values()) {
                  if (action.canRunInOtherThread()) return true;
              }
              if (this.defaultAction.canRunInOtherThread()) return true;
              return this.matchAction.canRunInOtherThread();
          });
      }
  
      public @NonNull Class<T> getKeyType() {
          return keyType;
      }
  
      public @NonNull TreeActionType getActionType() {
          return actionType;
      }
  
      public @NonNull String getGlobalId() {
          return globalId;
      }
  
      public abstract @NonNull Evaluator<T> getKey();
  
      public @NonNull Action getDefaultAction() {
          return defaultAction;
      }
  
      public @NonNull Action getMatchAction() {
          return matchAction;
      }
  
      public @NonNull TreeMap<T, Action> getActions() {
          return actions;
      }
  
      /**
       * 将基础类型动作的执行逻辑放入 BaseActionManager 是为了给其他插件覆写的机会
       */
      @Override
      protected @NonNull CompletableFuture<ActionResult> eval(
              @NonNull BaseActionManager manager,
              @NonNull ActionContext context
      ) {
          return manager.runAction(this, context);
      }
  
      public enum TreeActionType {
          LOWER,
          FLOOR,
          HIGHER,
          CEILING
      }
  }
  