// Global functions and macros that are available in all templates
// This file is loaded in at the very beginning

import org.bukkit.ChatColor;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.contents.TranslatableContents;
import net.minecraft.network.chat.TranslatableComponent;

import net.minecraft.world.level.storage.ValueOutput;
import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.level.storage.TagValueOutput;
import net.minecraft.world.level.storage.TagValueInput;

// Gets the text representation of a chat component (and only that component. It is not recursive.)
#if version >= 1.19
    #require net.minecraft.network.chat.Component public String getComponentText() {
        net.minecraft.network.chat.ComponentContents contents = instance.getContents();
        if (contents instanceof net.minecraft.network.chat.contents.PlainTextContents) {
            return ((net.minecraft.network.chat.contents.PlainTextContents) contents).text();
        } else if (contents instanceof TranslatableContents) {
  #if exists com.mohistmc.paper.adventure.PaperAdventure
            return com.mohistmc.paper.adventure.PaperAdventure.asPlain(com.mohistmc.paper.adventure.PaperAdventure.asAdventure(instance), null);
  #elseif exists io.papermc.paper.adventure.PaperAdventure
            return io.papermc.paper.adventure.PaperAdventure.asPlain(io.papermc.paper.adventure.PaperAdventure.asAdventure(instance), null);
  #else
            TranslatableContents trans = (TranslatableContents) contents;
            String key = trans.getKey();
            if (key == null) {
                return "null";
            }
            int namespaceOffset = 0;
            if (key.startsWith("block.minecraft.")) {
                namespaceOffset = 16;
            } else if (key.startsWith("item.minecraft.")) {
                namespaceOffset = 15;
            }
            key = key.substring(namespaceOffset);
            if (namespaceOffset > 0) {
                // Replace _ with " ", capitalize first letter
                key = key.replace('_', ' ');
                if (!key.isEmpty()) {
                    key = key.substring(0, 1).toUpperCase(java.util.Locale.ENGLISH) + key.substring(1);
                }
            }
            return key;
  #endif

  #if version >= 1.20.3
        } else if (contents != null && contents != net.minecraft.network.chat.CommonComponents.EMPTY) {
            // Unsupported / dont care
            return contents.toString();
  #else
        } else if (contents != null && contents != net.minecraft.network.chat.ComponentContents.EMPTY) {
            // Unsupported / dont care
            return contents.toString();
  #endif

        } else {
            return "";
        }
    }
#else
    #require net.minecraft.network.chat.Component public String getComponentText() {
        if (instance instanceof TranslatableComponent) {
            TranslatableComponent trans = (TranslatableComponent) instance;
            String key = trans.getKey();
            if (key == null) {
                return "null";
            }
            int namespaceOffset = 0;
            if (key.startsWith("block.minecraft.")) {
                namespaceOffset = 16;
            } else if (key.startsWith("item.minecraft.")) {
                namespaceOffset = 15;
            }
            key = key.substring(namespaceOffset);
            if (namespaceOffset > 0) {
                // Replace _ with " ", capitalize first letter
                key = key.replace('_', ' ');
                if (!key.isEmpty()) {
                    key = key.substring(0, 1).toUpperCase(java.util.Locale.ENGLISH) + key.substring(1);
                }
            }
            return key;
        } else {
  #if version >= 1.18
            return instance.getContents();
  #else
            return instance.getText();
  #endif
        }
    }
#endif

// Gets whether the text content of a component is empty and yields no legacy text at all - so can be ignored
#if version >= 1.19
    #require net.minecraft.network.chat.Component public boolean isComponentContentEmpty() {
        net.minecraft.network.chat.ComponentContents contents = instance.getContents();
        if (contents instanceof net.minecraft.network.chat.contents.PlainTextContents) {
            return ((net.minecraft.network.chat.contents.PlainTextContents) contents).text().isEmpty();
        } else {
            return contents == net.minecraft.network.chat.CommonComponents.EMPTY;
        }
    }
#elseif version >= 1.18
    #require net.minecraft.network.chat.Component public boolean isComponentContentEmpty() {
        return instance.getContents().isEmpty();
    }
#else
    #require net.minecraft.network.chat.Component public boolean isComponentContentEmpty() {
        return instance.getText().isEmpty();
    }
#endif

// Formats a chat component, and its siblings/children, as a legacy-color-formatted message String
#require net.minecraft.network.chat.Component public String formatAsLegacyString() {
    Component component = instance;

    // Some optimizations
    {
        java.util.List siblings = component.getSiblings();
        int numSiblings = siblings.size();

        // Due to immutable chat components, very often the root is a modifiable chat component
        // with empty text content and no defined style. This checks for that common case.
        // If there is no sibling or only one, resume with that one instead.
        if (numSiblings == 1 && component.getStyle().isEmpty()) {
            String text = component#getComponentText();
            if (text.isEmpty()) {
                component = (Component) siblings.get(0);
                siblings = component.getSiblings();
                numSiblings = siblings.size();
            }
        }

        // If component has default style, and no siblings, we can return the text value directly
        // This avoids the overhead of creating a stream and filling a StringBuilder
        if (numSiblings == 0 && component.getStyle().isEmpty()) {
            return component#getComponentText();
        }
    }

    StringBuilder out = new StringBuilder();
    java.util.Iterator iter = ((Iterable) component).iterator();

    ChatColor prev_bukkit_color = null;
    boolean prev_was_bold = false;
    boolean prev_was_italic = false;
    boolean prev_was_underlined = false;
    boolean prev_was_strikeThrough = false;
    boolean prev_was_obfuscated = false;

    while (iter.hasNext()) {
        Component c = (Component) iter.next();
        net.minecraft.network.chat.Style modi = c.getStyle();

        // Place reset character when too many style flags are active
        if ((prev_was_bold && !modi.isBold()) ||
            (prev_was_italic && !modi.isItalic()) ||
            (prev_was_underlined && !modi.isUnderlined()) ||
            (prev_was_strikeThrough && !modi.isStrikethrough()) ||
            (prev_was_obfuscated && !modi.isObfuscated()))
        {
            prev_bukkit_color = null;
            prev_was_bold = false;
            prev_was_italic = false;
            prev_was_underlined = false;
            prev_was_strikeThrough = false;
            prev_was_obfuscated = false;
            out.append(ChatFormatting.RESET);
        }

        if (modi.getColor() != null) {
            // Uses TextColorChatColorConversion
#if version >= 1.16
            #require net.minecraft.network.chat.Style public (ChatColor) net.minecraft.network.chat.TextColor getBukkitChatColor:getColor();
#else
            #require net.minecraft.network.chat.Style public (ChatColor) net.minecraft.ChatFormatting getBukkitChatColor:getColor();
#endif

            ChatColor bukkit_color = modi#getBukkitChatColor();
            if (bukkit_color != null) {
                // Track as 16 color format, omit duplicates
                if (prev_bukkit_color != bukkit_color) {
                    prev_bukkit_color = bukkit_color;
                    out.append(bukkit_color);
                }
            }

#if version >= 1.16
            if (bukkit_color == null) {
                // Get color RGB value
  #if version >= 1.17
                int rgb = modi.getColor().getValue();
  #else
                #require net.minecraft.network.chat.TextColor private final int chc_rgb:rgb;
                net.minecraft.network.chat.TextColor hexcolor = modi.getColor();
                int rgb = hexcolor#chc_rgb;
  #endif

                // Append hex color bungee style syntax
                // Technically we could use bungeecord ChatColor for this, but I don't know
                // whether all versions of spigot/paper/etc. actually include that class.
                final char style_char = com.bergerkiller.bukkit.common.utils.StringUtil.CHAT_STYLE_CHAR;
                out.append(style_char).append('x');
                int rgb_remaining = rgb;
                for (int n = 0; n < 6; n++) {
                    rgb_remaining <<= 4;
                    out.append(style_char).append(Character.forDigit((rgb_remaining >> 24) & 0xF, 16));
                }

                // Reset color format
                prev_bukkit_color = null;
            }
#endif
        }
        if (modi.isBold() && !prev_was_bold) {
            prev_was_bold = true;
            out.append(ChatFormatting.BOLD);
        }
        if (modi.isItalic() && !prev_was_italic) {
            prev_was_italic = true;
            out.append(ChatFormatting.ITALIC);
        }
        if (modi.isUnderlined() && !prev_was_underlined) {
            prev_was_underlined = true;
            out.append(ChatFormatting.UNDERLINE);
        }
        if (modi.isStrikethrough() && !prev_was_strikeThrough) {
            prev_was_strikeThrough = true;
            out.append(ChatFormatting.STRIKETHROUGH);
        }
        if (modi.isObfuscated() && !prev_was_obfuscated) {
            prev_was_obfuscated = true;
            out.append(ChatFormatting.OBFUSCATED);
        }

        String text = c#getComponentText();
        out.append(text);
    }
    return out.toString();
}

#require net.minecraft.network.chat.Component public static String[] formatLinesAsLegacyStrings(Component[] lines) {
    int numLines = lines.length;
    String[] legacyLines = new String[numLines];
    for (int i = 0; i < numLines; i++) {
        Component component = lines[i];
        legacyLines[i] = (component == null) ? "" : component#formatAsLegacyString();
    }
    return legacyLines;
}

// Creates a new Identifier from a key or namespace:key formatted String
#require net.minecraft.resources.Identifier public static Identifier parseIdentifier(String keyStr) {
#if version >= 1.21
    return Identifier.parse(keyStr);
#else
    return new Identifier(keyStr);
#endif
}

// This is all needed for serialization functions (save/load) on 1.21.6+
#if version >= 1.21.6
    #require net.minecraft.util.ProblemReporter public static com.bergerkiller.bukkit.common.internal.logic.ScopedProblemReporter createScopedProblemReporter() {
        return new com.bergerkiller.bukkit.common.internal.logic.ScopedProblemReporter(
                com.bergerkiller.bukkit.common.Logging.LOGGER);
    }

    #require TagValueOutput public static ValueOutput createTagValueOutput(net.minecraft.util.ProblemReporter problemreporter, net.minecraft.core.HolderLookup.Provider holderlookup_a, net.minecraft.nbt.CompoundTag nbttagcompound) {
        #require TagValueOutput static TagValueOutput tagValueOutputCtor:<init>(net.minecraft.util.ProblemReporter problemreporter, com.mojang.serialization.DynamicOps<net.minecraft.nbt.Tag> dynamicops, net.minecraft.nbt.CompoundTag nbttagcompound);

        com.mojang.serialization.DynamicOps dynamicops = holderlookup_a.createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE);
        return #tagValueOutputCtor(problemreporter, dynamicops, nbttagcompound);
    }

    #require TagValueOutput public static ValueOutput.TypedOutputList createTagListValueOutput(net.minecraft.util.ProblemReporter problemreporter, net.minecraft.core.HolderLookup.Provider holderlookup_a, com.mojang.serialization.Codec codec, net.minecraft.nbt.ListTag nbttaglist) {
        #require TagValueOutput.TypedListWrapper static TagValueInput.TypedListWrapper tagListOutputCtor:<init>(net.minecraft.util.ProblemReporter problemreporter, String s, com.mojang.serialization.DynamicOps<net.minecraft.nbt.Tag> dynamicops, com.mojang.serialization.Codec codec, net.minecraft.nbt.ListTag nbttaglist);

        com.mojang.serialization.DynamicOps dynamicops = holderlookup_a.createSerializationContext(net.minecraft.nbt.DynamicOpsNBT.INSTANCE);
        return (ValueOutput.TypedOutputList) #tagListOutputCtor(problemreporter, "", dynamicops, codec, nbttaglist);
    }

    #require TagValueInput public static ValueInput createTagValueInput:create(net.minecraft.util.ProblemReporter problemreporter, net.minecraft.core.HolderLookup.Provider holderlookup_a, net.minecraft.nbt.CompoundTag nbttagcompound);

    #require TagValueInput public static ValueInput.TypedInputList createTagListValueInput(net.minecraft.util.ProblemReporter problemreporter, net.minecraft.core.HolderLookup.Provider holderlookup_a, com.mojang.serialization.Codec codec, net.minecraft.nbt.ListTag nbttaglist) {
        net.minecraft.world.level.storage.ValueInputContextHelper context = new net.minecraft.world.level.storage.ValueInputContextHelper(holderlookup_a, net.minecraft.nbt.NbtOps.INSTANCE);

        if (nbttaglist.isEmpty()) {
            return context.emptyTypedList();
        }

        #require TagValueInput.TypedListWrapper static TagValueInput.TypedListWrapper tagListInputCtor:<init>(net.minecraft.util.ProblemReporter problemreporter, String s, net.minecraft.world.level.storage.ValueInputContextHelper context, com.mojang.serialization.Codec codec, net.minecraft.nbt.ListTag nbttaglist);
        return (ValueInput$TypedInputList) #tagListInputCtor(problemreporter, "", context, codec, nbttaglist);
    }
#endif
