package me.km.utils;

import com.mojang.brigadier.exceptions.CommandSyntaxException;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.entity.ai.attributes.AttributeModifier;
import net.minecraft.entity.ai.attributes.AttributeModifier.Operation;
import net.minecraft.inventory.EquipmentSlotType;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.JsonToNBT;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.nbt.StringNBT;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;

public class ItemStackUtils {
    // -----------------------------------------------------------------------------------
    // Attribute
    // -----------------------------------------------------------------------------------

    public enum Attribute {
        /**
         * value from 0 to 30;
         */
        ARMOR("generic.armor"),
        /**
         * value from 0 to 20;
         */
        ARMOR_TOUGHNESS("generic.armorToughness"),
        /**
         * value from 0 to 1.7E308;
         */
        ATTACK_DAMAGE("generic.attackDamage"),
        /**
         * value from 0 to 1;
         */
        KNOCKBACK_RESISTANCE("generic.knockbackResistance"),
        /**
         * value from 0 to 1.7E308;
         */
        MAX_HEALTH("generic.maxHealth"),
        /**
         * value from 0 to 1.7E308;
         */
        MOVEMENT_SPEED("generic.movementSpeed"),
        /**
         * value from 0 to 1024;
         */
        ATTACK_SPEED("generic.attackSpeed"),
        /**
         * value from -1024 to 1024;
         */
        LUCK("generic.luck");

        private final String name;

        Attribute(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }

    public static void addAttribute(ItemStack stack, Attribute a, EquipmentSlotType slot, double amount, Operation op) {
        stack.addAttributeModifier(a.getName(), new AttributeModifier("modifier", amount, op), slot);
    }

    // -----------------------------------------------------------------------------------
    // Item-Flags
    // -----------------------------------------------------------------------------------
    public enum ItemFlag {
        /**
         * Setting to show/hide enchants
         */
        HIDE_ENCHANTS,
        /**
         * Setting to show/hide Attributes like Damage
         */
        HIDE_ATTRIBUTES,
        /**
         * Setting to show/hide the unbreakable State
         */
        HIDE_UNBREAKABLE,
        /**
         * Setting to show/hide what the ItemStack can break/destroy
         */
        HIDE_DESTROYS,
        /**
         * Setting to show/hide where this ItemStack can be build/placed on
         */
        HIDE_PLACED_ON,
        /**
         * Setting to show/hide potion effects on this ItemStack
         */
        HIDE_POTION_EFFECTS;

        public byte getBit() {
            return (byte) (1 << this.ordinal());
        }
    }

    public static void addItemFlag(ItemStack stack, ItemFlag... flags) {
        CompoundNBT com = stack.hasTag() ? stack.getTag() : new CompoundNBT();
        if(com == null) // this will never happen, but the compiler wants it
        {
            return;
        }
        int i = com.getInt("HideFlags");
        for(ItemFlag f : flags) {
            i |= f.getBit(); // bitwise 'or'
        }
        com.putInt("HideFlags", i);
        stack.setTag(com);
    }

    public static void removeItemFlags(ItemStack stack, ItemFlag... flags) {
        CompoundNBT com = stack.hasTag() ? stack.getTag() : new CompoundNBT();
        if(com == null) // this will never happen, but the compiler wants it
        {
            return;
        }
        int i = com.getInt("HideFlags");
        for(ItemFlag f : flags) {
            i &= ~f.getBit(); // bitwise 'and' with inversed bits
        }
        com.putInt("HideFlags", i);
        stack.setTag(com);
    }

    public static boolean hasItemFlag(ItemStack stack, ItemFlag flag) {
        if(!stack.hasTag()) {
            return false;
        }
        CompoundNBT com = stack.getTag();
        if(com == null) // this will never happen, but the compiler wants it
        {
            return false;
        }
        byte b = flag.getBit();
        return (com.getInt("HideFlags") & b) == b;
    }

    public static String getNbtString(ItemStack stack) {
        return stack.write(new CompoundNBT()).toString();
    }

    public static ItemStack getStackFromNbtString(String s) throws CommandSyntaxException {
        CompoundNBT c = JsonToNBT.getTagFromJson(s);
        return ItemStack.read(c);
    }

    public static void setLore(ItemStack stack, List<Object> list) {
        CompoundNBT com = stack.getOrCreateChildTag("display");
        ListNBT nbtList = new ListNBT();
        list.forEach(s -> nbtList.add(StringNBT.valueOf(ITextComponent.Serializer.toJson(new StringTextComponent(s.toString())))));
        com.put("Lore", nbtList);
    }

    public static void addLore(ItemStack stack, String s) {
        CompoundNBT com = stack.getOrCreateChildTag("display");
        ListNBT nbtList = com.getList("Lore", 8);
        nbtList.add(StringNBT.valueOf(ITextComponent.Serializer.toJson(new StringTextComponent(s))));
        com.put("Lore", nbtList);
    }

    public static List<Object> getLore(ItemStack stack) {
        CompoundNBT com = stack.getOrCreateChildTag("display");
        ListNBT nbtList = com.getList("Lore", 8);
        ArrayList<Object> list = new ArrayList<>();
        for(int i = 0; i < nbtList.size(); i++) {
            list.add(ITextComponent.Serializer.fromJson(nbtList.getString(i)).getString());
        }
        return list;
    }

    public static void addLore(ItemStack stack, String s, int i) {
        CompoundNBT com = stack.getOrCreateChildTag("display");
        ListNBT list = com.getList("Lore", 8);
        if(i >= list.size()) {
            list.add(StringNBT.valueOf(ITextComponent.Serializer.toJson(new StringTextComponent(s))));
        } else {
            list.set(i, StringNBT.valueOf(ITextComponent.Serializer.toJson(new StringTextComponent(s))));
        }
        com.put("Lore", list);
    }
}
