package org.bukkit.craftbukkit.util;

import net.minecraft.network.chat.IChatBaseComponent;

import com.bergerkiller.generated.net.minecraft.network.chat.IChatBaseComponentHandle;
import com.bergerkiller.generated.org.bukkit.craftbukkit.util.LongObjectHashMapHandle;

class LongObjectHashMap {
#if version >= 1.14
    public static (LongObjectHashMapHandle) org.bukkit.craftbukkit.util.LongObjectHashMap createNew() {
        return new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap();
    }
#else
    public static (LongObjectHashMapHandle) org.bukkit.craftbukkit.util.LongObjectHashMap createNew() {
        return new LongObjectHashMap();
    }
#endif

    public void clear();

    public int size();

    public boolean containsKey(long key);

    public V get(long key);

    public V remove(long key);

    public V put(long key, V value);

#if version >= 1.14
    public abstract (Collection<V>) it.unimi.dsi.fastutil.objects.ObjectCollection<V> values();
    public abstract (Set<Long>) it.unimi.dsi.fastutil.longs.LongSet keySet();

    public V merge(long key, V value, java.util.function.BiFunction<?, ?, ?> remappingFunction);
    public V computeIfAbsent(long key, java.util.function.LongFunction<?> mappingFunction);
    public V getOrDefault(long key, V defaultValue);

    public (LongObjectHashMapHandle) org.bukkit.craftbukkit.util.LongObjectHashMap cloneMap() {
        return (org.bukkit.craftbukkit.util.LongObjectHashMap) com.bergerkiller.bukkit.common.utils.LogicUtil.clone(instance);
    }
#else
    public Collection<V> values();
    public Set<Long> keySet();

    // Doesn't exist, must be implemented
    public V merge(long key, V value, java.util.function.BiFunction<?, ?, ?> remappingFunction) {
        Object oldValue = instance.get(key);
        if (oldValue == null) {
            instance.put(key, value);
        } else {
            instance.put(key, remappingFunction.apply(oldValue, value));
        }
    }
    public V computeIfAbsent(long key, java.util.function.LongFunction<?> mappingFunction) {
        Object value = instance.get(key);
        if (value == null) {
            value = mappingFunction.apply(key);
            instance.put(key, value);
        }
        return value;
    }
    public V getOrDefault(long key, V defaultValue) {
        Object value = instance.get(key);
        return (value != null) ? value : defaultValue;
    }

    // Note: the builtin clone() has a runtime error, we can't use it at all :(
    public (LongObjectHashMapHandle) org.bukkit.craftbukkit.util.LongObjectHashMap cloneMap() {
        // Retrieve the data in the original hashmap
        // We can omit modCount
        #require org.bukkit.craftbukkit.util.LongObjectHashMap private transient long[][] keys;
        #require org.bukkit.craftbukkit.util.LongObjectHashMap private transient V[][] values;
        #require org.bukkit.craftbukkit.util.LongObjectHashMap private transient int size;
        long[][] keys = instance#keys;
        Object[][] values = instance#values;
        int size = instance#size;

        // Deep-clone the keys
        keys = (long[][]) keys.clone();
        for (int i = 0; i < keys.length; i++) {
            long[] arr = keys[i];
            if (arr != null) {
                keys[i] = (long[]) arr.clone();
            }
        }

        // Deep-clone the values
        values = (Object[][]) values.clone();
        for (int i = 0; i < values.length; i++) {
            Object[] arr = values[i];
            if (arr != null) {
                values[i] = (Object[]) arr.clone();
            }
        }

        // Create a new instance and apply the values
        LongObjectHashMap clone = new LongObjectHashMap();
        clone#keys = keys;
        clone#values = values;
        clone#size = size;
        return clone;
    }
#endif
}

class CraftMagicNumbers {
    #bootstrap com.bergerkiller.bukkit.common.internal.CommonBootstrap.initServer();

    public static org.bukkit.Material getMaterialFromBlock:getMaterial((Object) net.minecraft.world.level.block.Block nmsBlock);

    public static org.bukkit.Material getMaterialFromItem:getMaterial((Object) net.minecraft.world.item.Item nmsItem);

#if version >= 1.15.2
    // Workaround for bug since 1.15.2 with legacy materials
    public static (Object) net.minecraft.world.item.Item getItemFromMaterial(org.bukkit.Material material) {
        return CraftMagicNumbers.getItem(org.bukkit.craftbukkit.legacy.CraftLegacy.fromLegacy(material));
    }
    public static (Object) net.minecraft.world.level.block.Block getBlockFromMaterial(org.bukkit.Material material) {
        return CraftMagicNumbers.getBlock(org.bukkit.craftbukkit.legacy.CraftLegacy.fromLegacy(material));
    }
#else
    public static (Object) net.minecraft.world.item.Item getItemFromMaterial:getItem(org.bukkit.Material material);
    public static (Object) net.minecraft.world.level.block.Block getBlockFromMaterial:getBlock(org.bukkit.Material material);
#endif

    public static int getDataVersion() {
#if version >= 1.13
        return CraftMagicNumbers.INSTANCE.getDataVersion();
#else
        return 0;
#endif
    }

    <code>
    public static com.bergerkiller.generated.net.minecraft.world.level.block.state.IBlockDataHandle getBlockDataFromMaterial(org.bukkit.Material material) {
        return com.bergerkiller.generated.net.minecraft.world.level.block.BlockHandle.T.getBlockData.invoke(getBlockFromMaterial(material));
    }
    </code>

    public static boolean isItemMaterial(org.bukkit.Material material) {
#if version >= 1.13
        return material.isItem();
#else
        //TODO: How do we fix this?
        return true;
#endif
    }
}

class CraftChatMessage {

    // Original was modified so it does not erase a chat color style prefix character
    public static String fromComponent((IChatBaseComponentHandle) IChatBaseComponent component) {
        if (component == null) {
            return "";
        }

        // Macro defined in global.txt
        return component#formatAsLegacyString();
    }

    public static (IChatBaseComponentHandle[]) IChatBaseComponent[] fromString(String message, boolean keepNewlines) {
        IChatBaseComponent[] components = CraftChatMessage.fromString(message, keepNewlines);

        // Eliminate a common case of a root empty component with only one sibling inside
        // This is the case for most simple input messages
        if (components.length == 1) {
            IChatBaseComponent singleComponent = components[0];
            java.util.List siblings = singleComponent.getSiblings();
            if (siblings.size() == 1 && singleComponent.getStyle().isEmpty()) {
                String text = singleComponent#getComponentText();
                if (text.isEmpty()) {
                    components[0] = (IChatBaseComponent) siblings.get(0);
                }
            }
        }

        return components;
    }

    //public static (IChatBaseComponentHandle[]) IChatBaseComponent[] fromString(String message, boolean keepNewlines);
}