package pers.neige.neigeitems.hook.nms.impl;

import lombok.NonNull;
import net.minecraft.server.v1_12_R1.EntityPlayer;
import net.minecraft.server.v1_12_R1.MathHelper;
import net.minecraft.server.v1_12_R1.MinecraftKey;
import net.minecraft.server.v1_12_R1.RegistryMaterials;
import org.bukkit.Material;
import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_12_R1.util.CraftMagicNumbers;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import pers.neige.neigeitems.hook.nms.NamespacedKey;
import pers.neige.neigeitems.libs.bot.inker.bukkit.nbt.Nbt;
import pers.neige.neigeitems.libs.bot.inker.bukkit.nbt.NbtCompound;
import pers.neige.neigeitems.libs.bot.inker.bukkit.nbt.NbtList;
import pers.neige.neigeitems.libs.bot.inker.bukkit.nbt.NbtUtils;
import pers.neige.neigeitems.libs.bot.inker.bukkit.nbt.neigeitems.utils.EnchantmentUtils;
import pers.neige.neigeitems.utils.NumberParser;

import java.util.*;

/**
 * 1.12.2 版本, NamespacedKey 特殊兼容
 */
public class NMSHookerNamespacedKey extends NMSHookerCustomModelData {
    private final Map<Integer, Enchantment> enchantmentsById = new HashMap<>();
    private Material[] byId = new Material[383];

    public NMSHookerNamespacedKey() {
        for (Material material : Material.values()) {
            if (byId.length > material.getId()) {
                byId[material.getId()] = material;
            } else {
                byId = Arrays.copyOfRange(byId, 0, material.getId() + 2);
                byId[material.getId()] = material;
            }
        }
        for (Enchantment enchantment : Enchantment.values()) {
            enchantmentsById.put(EnchantmentUtils.getId(enchantment), enchantment);
        }
    }

    @Override
    protected Map<Material, NamespacedKey> loadNamespacedKeys() {
        RegistryMaterials<MinecraftKey, net.minecraft.server.v1_12_R1.Item> REGISTRY = net.minecraft.server.v1_12_R1.Item.REGISTRY;
        Map<Material, NamespacedKey> result = new HashMap<>();
        for (Material material : Material.values()) {
            try {
                net.minecraft.server.v1_12_R1.Item item = CraftMagicNumbers.getItem(material);
                if (item != null) {
                    MinecraftKey minecraftKey = REGISTRY.b(item);
                    result.put(material, new NamespacedKey(minecraftKey.b(), minecraftKey.getKey()));
                }
            } catch (Throwable ignored) {
            }
        }
        return result;
    }

    @Override
    public @Nullable Material getMaterial(@Nullable String material) {
        if (material == null) return null;
        Material result = Material.getMaterial(material.toUpperCase(Locale.ENGLISH));
        if (result != null) return result;
        Integer id = NumberParser.parseInteger(material);
        if (id == null) return null;
        return byId.length > id && id >= 0 ? byId[id] : null;
    }

    @Override
    public void giveExp(@NonNull Player player, int exp) {
        EntityPlayer realPlayer = ((CraftPlayer) player).getHandle();
        realPlayer.addScore(exp);
        realPlayer.exp += (float) exp / (float) realPlayer.getExpToLevel();
        realPlayer.expTotal = MathHelper.clamp(realPlayer.expTotal + exp, 0, Integer.MAX_VALUE);

        while (realPlayer.exp < 0.0F) {
            float f = realPlayer.exp * (float) realPlayer.getExpToLevel();
            if (realPlayer.expLevel > 0) {
                realPlayer.levelDown(-1);
                realPlayer.exp = 1.0F + f / (float) realPlayer.getExpToLevel();
            } else {
                realPlayer.levelDown(-1);
                realPlayer.exp = 0.0F;
            }
        }

        while (realPlayer.exp >= 1.0F) {
            realPlayer.exp = (realPlayer.exp - 1.0F) * (float) realPlayer.getExpToLevel();
            realPlayer.levelDown(1);
            realPlayer.exp /= (float) realPlayer.getExpToLevel();
        }
    }

    @Override
    protected @NonNull Map<Enchantment, Integer> buildEnchantments(@NonNull NbtList ench) {
        Map<Enchantment, Integer> enchantments = new LinkedHashMap<>(ench.size());

        for (Nbt<?> nbt : ench) {
            int id = ((NbtCompound) nbt).getShort(NbtUtils.getEnchantmentIdNbtKey());
            int level = ((NbtCompound) nbt).getShort(NbtUtils.getEnchantmentLvlNbtKey());
            Enchantment enchant = enchantmentsById.get(id);
            if (enchant != null) {
                enchantments.put(enchant, level);
            }
        }

        return enchantments;
    }
}