package com.hypixel.hytale.server.npc.role;

import com.hypixel.hytale.common.util.StringUtil;
import java.util.EnumSet;
import java.util.function.Supplier;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

public enum RoleDebugFlags implements Supplier<String> {
   TraceFail("Trace failed instructions"),
   TraceSuccess("Trace matched instructions"),
   TraceSensorFailures("Trace failing sensors"),
   Flock("Trace flock events"),
   FlockDamage("Trace flock damage events"),
   MotionControllerSteer("Trace steering activity of motion controllers"),
   Collisions("Trace collision information of motion controllers"),
   BlockCollisions("Trace collisions down to block level"),
   ProbeBlockCollisions("Trace collisions down to block level when probing"),
   MotionControllerMove("Trace movement activity of motion controllers"),
   ValidatePositions("Validate computed movement positions are not intersecting blocks"),
   SteeringRole("Debug blended steering behaviour from role like avoidance/separation"),
   DisplayState("Set display name to contents of state"),
   DisplayFlock("Set display name to flock state"),
   DisplayTime("Set display name to day time"),
   DisplayTarget("Set display name to locked target type"),
   DisplayAnim("Display animation state"),
   DisplayLightLevel("Display light levels"),
   DisplayCustom("Display custom debug information (generated by debug components)"),
   DisplayHP("Display NPC HP as numerical values"),
   DisplayStamina("Display NPC Stamina as numerical values"),
   Overlaps("Log overlapping blocks when validating position"),
   Pathfinder("Display pathfinder status"),
   DisplaySpeed("Display speed of entity"),
   DisplayFreeSlots("Display free inventory slots"),
   DisplayInternalId("Display the internal server ID for this entity"),
   DisplayName("Display the role name for this entity"),
   ValidateMath("Validate (some) math computations in movement"),
   VisAvoidance("Visualize avoidance vectors"),
   VisSeparation("Visualize separation vector"),
   VisAiming("Visualize aiming"),
   VisMarkedTargets("Visualize arrows to marked targets"),
   VisSensorRanges("Visualize entity sensor detection ranges"),
   VisLeashPosition("Visualize NPC leash position"),
   VisFlock("Visualize flock member connections"),
   BeaconMessages("Enable debugging of beacon message sending and receiving");

   private static final RoleDebugFlags.RoleDebugPreset[] presets = new RoleDebugFlags.RoleDebugPreset[]{
      new RoleDebugFlags.RoleDebugPreset("none", EnumSet.noneOf(RoleDebugFlags.class)),
      new RoleDebugFlags.RoleDebugPreset("all", EnumSet.allOf(RoleDebugFlags.class)),
      new RoleDebugFlags.RoleDebugPreset("move", EnumSet.of(MotionControllerMove, Collisions)),
      new RoleDebugFlags.RoleDebugPreset("steer", EnumSet.of(MotionControllerMove, MotionControllerSteer, Collisions)),
      new RoleDebugFlags.RoleDebugPreset("valid", EnumSet.of(MotionControllerMove, MotionControllerSteer, Collisions, ValidatePositions)),
      new RoleDebugFlags.RoleDebugPreset("block", EnumSet.of(MotionControllerMove, MotionControllerSteer, Collisions, BlockCollisions)),
      new RoleDebugFlags.RoleDebugPreset("visDist", EnumSet.of(VisAvoidance, VisSeparation)),
      new RoleDebugFlags.RoleDebugPreset("visSensorInfo", EnumSet.of(VisMarkedTargets, VisSensorRanges)),
      new RoleDebugFlags.RoleDebugPreset(
         "display",
         EnumSet.of(
            DisplayState,
            DisplayFlock,
            DisplayTime,
            DisplayTarget,
            DisplayAnim,
            DisplayLightLevel,
            DisplayCustom,
            DisplayHP,
            DisplayStamina,
            DisplaySpeed,
            DisplayFreeSlots,
            DisplayInternalId,
            DisplayName
         )
      ),
      new RoleDebugFlags.RoleDebugPreset("default", EnumSet.complementOf(EnumSet.of(ValidatePositions)))
   };
   private final String description;

   private RoleDebugFlags(String description) {
      this.description = description;
   }

   public String get() {
      return this.description;
   }

   @Nonnull
   public static EnumSet<RoleDebugFlags> getFlags(@Nonnull String[] args) {
      if (args.length == 0) {
         throw new IllegalArgumentException("Missing debug flags! " + getValidNameString());
      } else {
         EnumSet<RoleDebugFlags> flags = EnumSet.noneOf(RoleDebugFlags.class);

         for (String arg : args) {
            RoleDebugFlags debugFlag = StringUtil.parseEnum(RoleDebugFlags.class.getEnumConstants(), arg, StringUtil.MatchType.CASE_INSENSITIVE);
            if (debugFlag != null) {
               flags.add(debugFlag);
            } else {
               EnumSet<RoleDebugFlags> preset = findPreset(arg);
               if (preset == null) {
                  throw new IllegalArgumentException("Invalid flag/preset '" + arg + "'! " + getValidNameString());
               }

               flags.addAll(preset);
            }
         }

         return flags;
      }
   }

   @Nonnull
   public static StringBuilder getListOfFlags(@Nonnull EnumSet<RoleDebugFlags> flags, @Nonnull StringBuilder stringBuilder) {
      boolean comma = false;

      for (RoleDebugFlags flag : values()) {
         if (flags.contains(flag)) {
            if (comma) {
               stringBuilder.append(", ");
            }

            stringBuilder.append(flag);
            comma = true;
         }
      }

      return stringBuilder;
   }

   public static StringBuilder getListOfAllFlags(@Nonnull StringBuilder stringBuilder) {
      return getListOfFlags(EnumSet.allOf(RoleDebugFlags.class), stringBuilder);
   }

   public static StringBuilder getListOfAllPresets(@Nonnull StringBuilder stringBuilder) {
      boolean comma = false;

      for (RoleDebugFlags.RoleDebugPreset preset : presets) {
         if (comma) {
            stringBuilder.append(", ");
         }

         stringBuilder.append(preset.name);
         comma = true;
      }

      return stringBuilder;
   }

   @Nonnull
   public static EnumSet<RoleDebugFlags> getPreset(String arg) {
      EnumSet<RoleDebugFlags> preset = findPreset(arg);
      if (preset == null) {
         throw new IllegalArgumentException("Invalid flag/preset '" + arg + "'! " + getValidNameString());
      } else {
         EnumSet<RoleDebugFlags> flags = EnumSet.noneOf(RoleDebugFlags.class);
         flags.addAll(preset);
         return flags;
      }
   }

   @Nonnull
   private static String getValidNameString() {
      StringBuilder result = new StringBuilder();
      result.append("Valid presets are: ");
      boolean comma = false;

      for (RoleDebugFlags.RoleDebugPreset preset : presets) {
         if (comma) {
            result.append(", ");
         }

         result.append(preset.name);
         comma = true;
      }

      result.append(". Valid flags are: ");
      getListOfFlags(EnumSet.allOf(RoleDebugFlags.class), result);
      return result.toString();
   }

   @Nullable
   private static EnumSet<RoleDebugFlags> findPreset(String name) {
      for (RoleDebugFlags.RoleDebugPreset preset : presets) {
         if (preset.name.equalsIgnoreCase(name)) {
            return preset.config;
         }
      }

      return null;
   }

   public static boolean havePreset(String name) {
      for (RoleDebugFlags.RoleDebugPreset preset : presets) {
         if (preset.name.equalsIgnoreCase(name)) {
            return true;
         }
      }

      return false;
   }

   private static class RoleDebugPreset {
      public String name;
      public EnumSet<RoleDebugFlags> config;

      private RoleDebugPreset(String name, EnumSet<RoleDebugFlags> config) {
         this.name = name;
         this.config = config;
      }
   }
}
