content: package org.allaymc.api.utils;
  
  import java.util.HashMap;
  import java.util.Map;
  import java.util.TreeMap;
  import java.util.regex.Pattern;
  
  /**
   * All supported formatting values for chat and console.
   *
   * @author daoge_cmd | Nukkit
   * @see <a href="https://minecraft.wiki/w/Formatting_codes#Color_codes">Color codes</a>
   */
  public enum TextFormat {
      BLACK('0', 0x00),
      DARK_BLUE('1', 0x1),
      DARK_GREEN('2', 0x2),
      DARK_AQUA('3', 0x3),
      DARK_RED('4', 0x4),
      DARK_PURPLE('5', 0x5),
      GOLD('6', 0x6),
      GRAY('7', 0x7),
      DARK_GRAY('8', 0x8),
      BLUE('9', 0x9),
      GREEN('a', 0xA),
      AQUA('b', 0xB),
      RED('c', 0xC),
      LIGHT_PURPLE('d', 0xD),
      YELLOW('e', 0xE),
      WHITE('f', 0xF),
      MINECOIN_GOLD('g', 0x16),
      MATERIAL_QUARTZ('h', 0x17),
      MATERIAL_IRON('i', 0x18),
      MATERIAL_NETHERITE('j', 0x19),
      MATERIAL_REDSTONE('m', 0x20),
      MATERIAL_COPPER('n', 0x21),
      MATERIAL_GOLD('p', 0x22),
      MATERIAL_EMERALD('q', 0x23),
      MATERIAL_DIAMOND('s', 0x24),
      MATERIAL_LAPIS('t', 0x25),
      MATERIAL_AMETHYST('u', 0x26),
      MATERIAL_RESIN('v', 0x27),
      OBFUSCATED('k', 0x10, true),
      BOLD('l', 0x11, true),
      ITALIC('o', 0x14, true),
      RESET('r', 0x15);
  
      /**
       * The special character which prefixes all format codes. Use this if
       * you need to dynamically convert format codes from your custom format.
       */
      public static final char ESCAPE = '§';
  
      private static final Pattern CLEAN_PATTERN = Pattern.compile("(?i)" + ESCAPE + "[0-9A-LO-U]");
  
      private final static Map<Integer, TextFormat> BY_ID = new TreeMap<>();
      private final static Map<Character, TextFormat> BY_CHAR = new HashMap<>();
  
      static {
          for (TextFormat color : values()) {
              BY_ID.put(color.intCode, color);
              BY_CHAR.put(color.code, color);
          }
      }
  
      private final int intCode;
      private final char code;
      private final boolean isFormat;
      private final String toString;
  
      TextFormat(char code, int intCode) {
          this(code, intCode, false);
      }
  
      TextFormat(char code, int intCode, boolean isFormat) {
          this.code = code;
          this.intCode = intCode;
          this.isFormat = isFormat;
          this.toString = new String(new char[]{ESCAPE, code});
      }
  
      /**
       * Gets the TextFormat represented by the specified format code.
       *
       * @param intCode Code to check
       *
       * @return Associative {@link TextFormat} with the given code, or {@code null} if it doesn't exist
       */
      public static TextFormat getById(int intCode) {
          return BY_ID.get(intCode);
      }
  
      /**
       * Gets the TextFormat represented by the specified format code.
       *
       * @param code Code to check
       *
       * @return Associative {@link TextFormat} with the given code, or {@code null} if it doesn't exist
       */
      public static TextFormat getByChar(char code) {
          return BY_CHAR.get(code);
      }
  
      /**
       * Gets the TextFormat represented by the specified format code.
       *
       * @param code Code to check
       *
       * @return Associative {@link TextFormat} with the given code, or {@code null} if it doesn't exist
       */
      public static TextFormat getByChar(String code) {
          if (code == null || code.length() <= 1) return null;
          return BY_CHAR.get(code.charAt(0));
      }
  
      /**
       * Cleans the given message of all format codes.
       *
       * @param input String to clean
       *
       * @return A copy of the input string, without any formatting
       */
      public static String clean(String input) {
          return clean(input, false);
      }
  
      public static String clean(String input, boolean recursive) {
          if (input == null) return null;
  
          var result = CLEAN_PATTERN.matcher(input).replaceAll("");
          if (recursive && CLEAN_PATTERN.matcher(result).find()) {
              return clean(result, true);
          }
  
          return result;
      }
  
      /**
       * Translates a string using an alternate format code character into a
       * string that uses the internal {@code TextFormat.ESCAPE} format code
       * character. The alternate format code character will only be replaced if
       * it is immediately followed by 0-9, A-G, a-g, K-O, k-o, R or r.
       *
       * @param altFormatChar   The alternate format code character to replace. Ex: &amp;
       * @param textToTranslate Text containing the alternate format code character
       *
       * @return Text containing the {@code TextFormat.ESCAPE} format code character
       */
      public static String colorize(char altFormatChar, String textToTranslate) {
          var text = textToTranslate.toCharArray();
          for (int i = 0; i < text.length - 1; i++) {
              if (text[i] == altFormatChar && "0123456789AaBbCcDdEeFfGgHhIiJjMmNnPpQqSsTtUuKkLlOoRr".indexOf(text[i + 1]) > -1) {
                  text[i] = TextFormat.ESCAPE;
                  text[i + 1] = Character.toLowerCase(text[i + 1]);
              }
          }
  
          return new String(text);
      }
  
      /**
       * Translates a string, using an ampersand (&amp;) as an alternate format code
       * character, into a string that uses the internal TextFormat. ESCAPE format
       * code character. The alternate format code character will only be replaced if
       * it is immediately followed by 0-9, A-G, a-g, K-O, k-o, R or r.
       *
       * @param textToTranslate Text containing the alternate format code character
       *
       * @return Text containing the TextFormat. ESCAPE format code character
       */
      public static String colorize(String textToTranslate) {
          return colorize('&', textToTranslate);
      }
  
      /**
       * Gets the chat color used at the end of the given input string.
       *
       * @param input Input string to retrieve the colors from
       *
       * @return Any remaining chat color to pass onto the next line
       */
      public static String getLastColors(String input) {
          StringBuilder result = new StringBuilder();
          int length = input.length();
  
          // Search backwards from the end as it is faster
          for (int index = length - 1; index > -1; index--) {
              char section = input.charAt(index);
              if (section == ESCAPE && index < length - 1) {
                  char c = input.charAt(index + 1);
                  TextFormat color = getByChar(c);
  
                  if (color == null) continue;
                  result.insert(0, color);
  
                  // Once we find a color or reset we can stop searching
                  if (color.isColor() || color.equals(RESET)) break;
              }
          }
  
          return result.toString();
      }
  
      /**
       * Gets the char value associated with this color
       *
       * @return A char value of this color code
       */
      public char getChar() {
          return code;
      }
  
      @Override
      public String toString() {
          return toString;
      }
  
      /**
       * Checks if this code is a format code as opposed to a color code.
       */
      public boolean isFormat() {
          return isFormat;
      }
  
      /**
       * Checks if this code is a color code as opposed to a format code.
       */
      public boolean isColor() {
          return !isFormat && this != RESET;
      }
  }
  