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

import net.minecraft.EnumChatFormat;
import net.minecraft.network.chat.ChatModifier;
import net.minecraft.network.chat.contents.TranslatableContents;

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.IChatBaseComponent public String getComponentText() {
        net.minecraft.network.chat.ComponentContents contents = instance.getContents();
        if (contents instanceof net.minecraft.network.chat.contents.LiteralContents) {
            return ((net.minecraft.network.chat.contents.LiteralContents) 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 "";
        }
    }

    #require net.minecraft.network.chat.IChatBaseComponent public boolean isComponentContentEmpty() {
        net.minecraft.network.chat.ComponentContents contents = instance.getContents();
        if (contents instanceof net.minecraft.network.chat.contents.LiteralContents) {
            return ((net.minecraft.network.chat.contents.LiteralContents) contents).text().isEmpty();
        } else {
            return contents == net.minecraft.network.chat.CommonComponents.EMPTY;
        }
    }
#elseif version >= 1.18
    #require net.minecraft.network.chat.IChatBaseComponent public abstract String getComponentText:getContents();
    #require net.minecraft.network.chat.IChatBaseComponent public boolean isComponentContentEmpty() {
        return instance.getContents().isEmpty();
    }
#else
    #require net.minecraft.network.chat.IChatBaseComponent public abstract String getComponentText:getText();
    #require net.minecraft.network.chat.IChatBaseComponent 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.IChatBaseComponent public String formatAsLegacyString() {
    IChatBaseComponent 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 = (IChatBaseComponent) 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();

    EnumChatFormat prev_color_format = 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()) {
        IChatBaseComponent c = (IChatBaseComponent) iter.next();
        ChatModifier 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_color_format = null;
            prev_was_bold = false;
            prev_was_italic = false;
            prev_was_underlined = false;
            prev_was_strikeThrough = false;
            prev_was_obfuscated = false;
            out.append(EnumChatFormat.RESET);
        }

        if (modi.getColor() != null) {
#if version >= 1.16
            EnumChatFormat color_format = modi.getColor().format;
            if (color_format != null) {
                // Track as 16 color format, omit duplicates
                if (prev_color_format != color_format) {
                    prev_color_format = color_format;
                    out.append(color_format);
                }
            } else {
                // Get color RGB value
  #if version >= 1.17
                int rgb = modi.getColor().getValue();
  #else
                #require net.minecraft.network.chat.ChatHexColor private final int chc_rgb:rgb;
                net.minecraft.network.chat.ChatHexColor 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_color_format = null;
            }
#else
            out.append(modi.getColor());
#endif
        }
        if (modi.isBold() && !prev_was_bold) {
            prev_was_bold = true;
            out.append(EnumChatFormat.BOLD);
        }
        if (modi.isItalic() && !prev_was_italic) {
            prev_was_italic = true;
            out.append(EnumChatFormat.ITALIC);
        }
        if (modi.isUnderlined() && !prev_was_underlined) {
            prev_was_underlined = true;
            out.append(EnumChatFormat.UNDERLINE);
        }
        if (modi.isStrikethrough() && !prev_was_strikeThrough) {
            prev_was_strikeThrough = true;
            out.append(EnumChatFormat.STRIKETHROUGH);
        }
        if (modi.isObfuscated() && !prev_was_obfuscated) {
            prev_was_obfuscated = true;
            out.append(EnumChatFormat.OBFUSCATED);
        }

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

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

// Creates a new MinecraftKey from a key or namespace:key formatted String
#require net.minecraft.resources.MinecraftKey public static MinecraftKey parseMinecraftKey(String keyStr) {
#if version >= 1.21
    return MinecraftKey.parse(keyStr);
#else
    return new MinecraftKey(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.a holderlookup_a, net.minecraft.nbt.NBTTagCompound nbttagcompound) {
        #require TagValueOutput static TagValueOutput tagValueOutputCtor:<init>(net.minecraft.util.ProblemReporter problemreporter, com.mojang.serialization.DynamicOps<net.minecraft.nbt.NBTBase> dynamicops, net.minecraft.nbt.NBTTagCompound nbttagcompound);

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

    #require TagValueOutput public static ValueOutput.TypedOutputList createTagListValueOutput(net.minecraft.util.ProblemReporter problemreporter, net.minecraft.core.HolderLookup.a holderlookup_a, com.mojang.serialization.Codec codec, net.minecraft.nbt.NBTTagList nbttaglist) {
        #require TagValueOutput.TypedListWrapper static TagValueInput.TypedListWrapper tagListOutputCtor:<init>(net.minecraft.util.ProblemReporter problemreporter, String s, com.mojang.serialization.DynamicOps<net.minecraft.nbt.NBTBase> dynamicops, com.mojang.serialization.Codec codec, net.minecraft.nbt.NBTTagList 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.a holderlookup_a, net.minecraft.nbt.NBTTagCompound nbttagcompound);

    #require TagValueInput public static ValueInput.TypedInputList createTagListValueInput(net.minecraft.util.ProblemReporter problemreporter, net.minecraft.core.HolderLookup.a holderlookup_a, com.mojang.serialization.Codec codec, net.minecraft.nbt.NBTTagList nbttaglist) {
        net.minecraft.world.level.storage.ValueInputContextHelper context = new net.minecraft.world.level.storage.ValueInputContextHelper(holderlookup_a, net.minecraft.nbt.DynamicOpsNBT.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.NBTTagList nbttaglist);
        return (ValueInput$TypedInputList) #tagListInputCtor(problemreporter, "", context, codec, nbttaglist);
    }
#endif
