Hub / src / main / java / com / lifeknight / relaymchub / player / EditCustomKitGUI.java
EditCustomKitGUI.java
Raw
package com.lifeknight.relaymchub.player;

import com.lifeknight.relaymchub.Main;
import com.lifeknight.relaymcutils.RelayMCUtils;
import com.lifeknight.relaymcutils.player.*;
import com.lifeknight.relayutils.basic.Miscellaneous;
import com.lifeknight.relayutils.basic.SwearUtils;
import com.lifeknight.relayutils.basic.Text;
import com.lifeknight.relayutils.filter.ChatFilter;
import com.lifeknight.relayutils.player.Group;
import com.lifeknight.relayutils.utilities.ItemUtilities;
import net.kyori.adventure.text.Component;
import org.bukkit.*;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
import org.bukkit.inventory.meta.FireworkMeta;
import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.potion.PotionData;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.potion.PotionType;

import java.util.*;
import java.util.function.Consumer;

import static org.bukkit.ChatColor.*;

public class EditCustomKitGUI extends SmartGUI {
    public static final String DEFAULT_NAME = "Untitled Kit";

    public static final int HELMET = 10;
    public static final int CHESTPLATE = 11;
    public static final int LEGGINGS = 12;
    public static final int BOOTS = 13;

    public static final int OFF_HAND = 15;
    public static final int REPRESENTATIVE = -7;

    public static final int INVENTORY_START = 18;
    public static final int INVENTORY_END = 44;

    public static final int HOTBAR_START = 45;
    public static final int HOTBAR_END = 53;

    public final HubPlayer owner;
    public final Kit originalKit;
    public String id;

    protected final CustomDuelsKit customDuelsKit;
    protected final HubGameSettings gameSettings;
    private final String originalId;

    boolean shared = false;

    public String name = DEFAULT_NAME;

    protected ItemStack representative = new ItemStack(Material.RED_BANNER);
    protected ItemStack helmet = null;
    protected ItemStack chestplate = null;
    protected ItemStack leggings = null;
    protected ItemStack boots = null;
    protected ItemStack offHand = null;

    protected ItemStack[] inventory = new ItemStack[27];
    protected ItemStack[] hotbar = new ItemStack[9];

    protected ItemStack clipboard = null;
    protected DuelMap map = null;
    protected String customMap = null;
    private final List<UUID> creators = new ArrayList<>();
    public String publicCustomMap = null;
    private boolean deleted = false;

    public EditCustomKitGUI(CustomDuelsKit customDuelsKit, HubPlayer owner, int id) {
        super("Custom Kit", 6, false);
        this.customDuelsKit = customDuelsKit;
        this.originalKit = customDuelsKit == null ? null : customDuelsKit.getKit();
        if (this.customDuelsKit != null) {
            if (!this.customDuelsKit.getOwner().equals(owner.getUUID())) {
                this.creators.add(this.customDuelsKit.getOwner());
            }
            for (UUID creator : this.customDuelsKit.getCreators()) {
                if (!creator.equals(owner.getUUID())) {
                    creators.add(creator);
                }
            }
            if (this.customDuelsKit.getPublicCustomMap() != null && !this.customDuelsKit.getPublicCustomMap().isEmpty()) {
                this.publicCustomMap = this.customDuelsKit.getPublicCustomMap();
            }
        }
        this.originalId = this.customDuelsKit == null ? null : this.customDuelsKit.getId();
        this.owner = owner;
        this.gameSettings = HubPlayer.getDefaultCustomDuelsGameSettings();
        if (customDuelsKit != null) {
            this.gameSettings.applyFromJson(customDuelsKit.getGameSettings());
            if (!customDuelsKit.getMaps().isEmpty()) {
                this.map = DuelMap.getDuelMap(customDuelsKit.getMaps().get(0));
                if (this.map == null) {
                    this.customMap = customDuelsKit.getMaps().get(0);
                }
            }
        }

        if (this.customDuelsKit != null) {
            this.name = customDuelsKit.getKit().name;
            if (this.customDuelsKit.isVisible()) {
                this.id = CustomDuelsKit.generateNewId();
            } else {
                this.id = customDuelsKit.getId();
            }
        } else {
            this.id = CustomDuelsKit.generateNewId();
            this.name = "Kit #" + (this.owner.getCustomDuelKits().size() + 1);
        }

        this.setFromOriginal();
        this.fill();

        this.owner.openGui(this);
        this.owner.setLastCustomKitGui(this);
    }

    public ItemStack getFromKitItem(KitItem kitItem) {
        return kitItem == null ? null : kitItem.itemStack;
    }

    public void setFromKit(Kit kit) {
        this.helmet = this.getFromKitItem(kit.helmet);
        this.chestplate = this.getFromKitItem(kit.chestplate);
        this.leggings = this.getFromKitItem(kit.leggings);
        this.boots = this.getFromKitItem(kit.boots);
        this.offHand = null;
        if (kit.representative != null) {
            this.representative = this.getFromKitItem(kit.representative);
        }

        this.inventory = new ItemStack[27];
        this.hotbar = new ItemStack[9];

        for (KitItem item : kit.items) {
            if (item.slot >= 0) {
                if (item.slot < 9) {
                    this.hotbar[item.slot] = item.itemStack;
                } else if (item.slot < 36) {
                    this.inventory[item.slot - 9] = item.itemStack;
                } else if (item.slot == 40) {
                    this.offHand = item.itemStack;
                }
            }
        }
        this.fill();
    }

    public void setFromOriginal() {
        if (this.hasOriginal()) {
            this.setFromKit(this.originalKit);
        } else {
            this.representative = new ItemStack(Material.RED_BANNER);
            this.helmet = null;
            this.chestplate = null;
            this.leggings = null;
            this.boots = null;
            this.offHand = null;

            this.inventory = new ItemStack[27];
            this.hotbar = new ItemStack[9];
        }
    }

    public void fill() {
        this.owner.isCreatingCustomKit = true;
        ItemStack armorEmpty = new ItemStack(Material.RED_STAINED_GLASS_PANE);

        this.add(HELMET, this.helmet == null ? armorEmpty : this.helmet.clone(), (player, b, b2) -> this.editSlot(HELMET, b, b2), this.helmet == null ? GOLD + "Helmet" : "");
        this.add(CHESTPLATE, this.chestplate == null ? armorEmpty : this.chestplate.clone(), (player, b, b2) -> this.editSlot(CHESTPLATE, b, b2), this.chestplate == null ? GOLD + "Chestplate" : this.chestplate.getItemMeta().getDisplayName());
        this.add(LEGGINGS, this.leggings == null ? armorEmpty : this.leggings.clone(), (player, b, b2) -> this.editSlot(LEGGINGS, b, b2), this.leggings == null ? GOLD + "Leggings" : this.leggings.getItemMeta().getDisplayName());
        this.add(BOOTS, this.boots == null ? armorEmpty : this.boots.clone(), (player, b, b2) -> this.editSlot(BOOTS, b, b2), this.boots == null ? GOLD + "Boots" : this.boots.getItemMeta().getDisplayName());

        this.add(OFF_HAND, this.offHand == null ? new ItemStack(Material.LIME_STAINED_GLASS_PANE) : this.offHand.clone(), (player, b, b2) -> this.editSlot(OFF_HAND, b, b2), this.offHand == null ? AQUA + "Offhand" : this.offHand.getItemMeta().getDisplayName());

        //this.add(REPRESENTATIVE, this.representative == null ? armorEmpty : this.representative.clone(), (player, b, b2) -> this.editSlot(REPRESENTATIVE, b, b2), RED + "Kit Icon");

        for (int i = INVENTORY_START; i <= INVENTORY_END; i++) {
            int finalI = i;
            int index;
            if (finalI < 27) {
                index = finalI % 9 + 18;
            } else if (finalI < 36) {
                index = finalI % 9 + 9;
            } else {
                index = finalI % 9;
            }
            ItemStack itemStack = this.inventory[index];
            this.add(i, itemStack == null ? new ItemStack(Material.LIGHT_BLUE_STAINED_GLASS_PANE) : itemStack, (player, b, b2) -> this.editSlot(finalI, b, b2), itemStack == null ? YELLOW + "Inventory" : itemStack.getItemMeta().getDisplayName());
        }

        for (int i = HOTBAR_START; i <= HOTBAR_END; i++) {
            int finalI = i;
            ItemStack itemStack = this.hotbar[finalI % 9];
            this.add(i, itemStack == null ? new ItemStack(Material.BLUE_STAINED_GLASS_PANE) : itemStack, (player, b, b2) -> this.editSlot(finalI, b, b2), itemStack == null ? LIGHT_PURPLE + "Hotbar" : itemStack.getItemMeta().getDisplayName());
        }

        this.add(0, Material.PAINTING, (player, b, b2) -> {
            {
                this.close(this.owner.getPlayer());
                this.owner.sendSuccessMessage("Your kit has been saved and selected.");
                this.owner.playSuccessSound();
                this.owner.isCreatingCustomKit = false;
            }
        }, GREEN + "Save");
        this.add(1, Material.WRITABLE_BOOK, (player, b, b1) -> {
            if (this.owner.shareCustomKit(this.getAsKit(), this.originalId)) {
                this.shared = true;
                this.owner.closeInventory();
            }
        }, GREEN + "Share Kit", GRAY + "Share this kit.");

        this.add(17, Material.BOOK, (player, b, b2) -> {
                }, LIGHT_PURPLE + "Usage",
                GRAY + "Left-Click a slot to set an item.",
                GRAY + "Right-Click a slot to edit an item.",
                GRAY + "Right-Click an empty slot to use a preset.",
                GRAY + "Shift Right-Click a slot to copy or paste.",
                GRAY + "Shift Left-Click a slot to clear.");
        this.add(2, Material.NAME_TAG, (player, b, b2) -> this.showLabelMenu(), AQUA + "Edit Label", GRAY + "Change the name and icon of this kit.");
/*
        this.add(1, Material.WRITABLE_BOOK, (player, b, b2) -> {
                    this.showLabelMenu();
                }, LIGHT_PURPLE + "Label",
                GRAY + "Change the name and kit icon of this kit.");
*/
        //this.add(5, Material.NETHERITE_SCRAP, (player, b, b2) -> this.reset(), YELLOW + "Reset", GRAY + "Revert this kit to its state before you changed anything.");
        //this.add(6, Material.BARRIER, (player, b, b2) -> this.cancel(), RED + "Cancel");
        this.add(5, Material.COMMAND_BLOCK, (player, b, b1) -> this.editCreative(), GOLD + "Creative Editor", GRAY + "Edit your kit in creative mode.");
        this.add(6, Material.MAP, (player, b, b1) -> this.selectMap(), GOLD + "Map", GRAY + "Select a map to play on.");
        this.add(7, Material.COMPARATOR, (player, b, b2) -> this.editSettings(), AQUA + "Settings", GRAY + "Edit game settings.");
        this.add(8, Material.REDSTONE, (player, b, b2) -> this.delete(), DARK_RED + "Delete");
    }

    protected void editCreative() {
        this.close(this.owner.getPlayer());
        this.owner.getSmartPlayer().clear();
        this.getAsKit().getKit().giveItemsToPlayer(this.owner.getPlayer(), true);
        this.owner.getSmartPlayer().setGameMode(GameMode.CREATIVE);
        RelayMCUtils.scheduleSyncDelayedTask(() -> this.owner.setCreatingCustomKitCreative(true), 0.1);
        this.owner.sendInfoMessage("Open your inventory to begin editing your kit.");
        this.owner.sendMessage("%sOnce you have finished, close your inventory to the Kit Editor.", AQUA);
    }

    protected void editSettings() {
        this.owner.openGui(this.gameSettings.getGUI(this.owner.getHighestGroup(), this));
        this.owner.playClickSound();
    }

    protected void selectMap() {
        SmartGUI mapSelector = new SmartGUI("Map", 6, false) {
            @Override
            public void onClose() {
                save();
            }

            @Override
            public void open(Player player) {
                this.fill();
                super.open(player);
            }

            public void fill() {
                clearAll();
                for (int i = 0; i < DuelMap.getDuelMaps().length; i++) {
                    DuelMap duelMap = DuelMap.getDuelMaps()[i];
                    int row = i / 7;
                    int index = 9 * (row + 1) + i % 7 + 1;
                    add(index, map == duelMap ? ItemUtilities.enchanted(duelMap.getRepresentative()) : new ItemStack(duelMap.getRepresentative()), (player, b, b1) -> {
                        if (map != duelMap) {
                            map = duelMap;
                            customMap = null;
                            fill();
                        }
                        owner.playSuccessSound();
                    }, AQUA + duelMap.getName());
                }

                add(0, Material.ARROW, (player, b, b1) -> back(), RED + "Back");

                final int startIndex = 45;
                for (int i = 0; i < 5; i++) {
                    List<CustomMap> customMaps = Miscellaneous.getList(owner.getCustomMaps());
                    if (i < customMaps.size()) {
                        CustomMap customMapObj = customMaps.get(i);
                        add(startIndex + i, customMapObj.getId().equals(customMap) ? ItemUtilities.enchanted(customMapObj.getRepresentative()) : new ItemStack(customMapObj.getRepresentative()), (player, b, b1) -> {
                            if (b1) {
                                owner.editSocialSettings(customMapObj, false);
                            } else {
                                if (b) {
                                    owner.editCustomMap(customMapObj, false);
                                    customMap = customMapObj.getId();
                                    map = null;
                                } else {
                                    if (!customMapObj.getId().equalsIgnoreCase(customMap)) {
                                        customMap = customMapObj.getId();
                                        map = null;
                                        fill();
                                    }
                                    owner.playSuccessSound();
                                }
                            }
                        }, AQUA + customMapObj.getName(), (customMapObj.getId().equalsIgnoreCase(customMap) ? GREEN + "Selected" : GRAY + "Left-click to select."), GRAY + "Right-click to edit.", GRAY + "Shift-click to edit settings.");
                    } else {
                        if (i == 0 || owner.isInGroupOrHigher(Group.CHAMPION)) {
                            add(startIndex + i, Material.OAK_BUTTON, (((player, b, b1) -> createCustomMap())), GREEN + "Create Custom Map", GRAY + "Make your own map.");
                        } else {
                            add(startIndex + i, Material.STONE_BUTTON, (player, b, b1) -> owner.playErrorSound(), ChatColor.RED + ChatColor.BOLD.toString() + "CREATE MAP", GRAY + ITALIC.toString() + "Upgrade to Premium to unlock this slot.");
                        }
                    }
                }

                final boolean[] hasPublicMap = {publicCustomMap != null};
                CustomMap publicCustomMapObj;
                if (hasPublicMap[0]) {
                    publicCustomMapObj = CustomMap.hasCustomMap(publicCustomMap) ? CustomMap.getCustomMap(publicCustomMap) : null;
                } else {
                    publicCustomMapObj = null;
                }
                if (hasPublicMap[0] && publicCustomMapObj != null) {
                    List<String> description = new ArrayList<>();

                    boolean isSelected = Text.comparablyEquals(publicCustomMap, customMap);
                    if (isSelected) {
                        description.add(GREEN + "SELECTED");
                    }

                    description.add(GRAY + "The public custom map you have for this kit.");
                    description.add(GRAY + "Right-Click to to preview.");

                    add(51, isSelected ? ItemUtilities.enchanted(publicCustomMapObj.getRepresentative()) : new ItemStack(publicCustomMapObj.getRepresentative()), (player, b, b1) -> {

                        if (b) {
                            owner.selectPublicCustomMap(EditCustomKitGUI.this, publicCustomMapObj, true);
                        } else {
                            if (!isSelected) {
                                EditCustomKitGUI.this.publicCustomMap = publicCustomMapObj.getId();
                                customMap = publicCustomMapObj.getId();
                                owner.playSuccessSound();
                                map = null;
                                fill();
                            }
                        }
                    }, AQUA + publicCustomMapObj.getName(), description);
                } else {
                    add(51, Material.POLISHED_BLACKSTONE_BUTTON, (player, b, b1) -> {
                        enterCustomMapCode();
                    }, YELLOW + "Public Custom Map", GRAY + "Click to enter a custom map code.");
                }
                add(52, Material.EMERALD, (player, b, b1) -> owner.showCustomMapHistory(EditCustomKitGUI.this), GREEN + "History", GRAY + "View your recently played maps.");
                add(53, Material.CLOCK, (player, b, b1) -> owner.browsePublicMaps(EditCustomKitGUI.this, false), GOLD + "Public Maps", GRAY + "Browse public custom maps.");

                fillEmptySpots();
            }
        };

        mapSelector.open(this.owner.getPlayer());
        this.owner.playClickSound();
    }

    private void enterCustomMapCode() {
        getStringInput("Enter Code", null, s -> {
            if (!s.isEmpty()) {
                Main.async(() -> {

                    CustomMap customMap1 = CustomMap.getCustomMap(s);
                    if (customMap1 != null) {
                        this.publicCustomMap = customMap1.getId();
                        this.owner.playSuccessSound();
                    } else {
                        this.owner.playErrorSound();
                        this.owner.sendErrorMessage("No public map by the code %s%s%s found.", YELLOW, s, RED);
                    }
                });
            }

            selectMap();
        });
    }

    public void createCustomMap() {
        CustomMap customMap = new CustomMap(this.owner.getSmartPlayer());
        this.owner.editCustomMap(customMap, true);
        this.customMap = customMap.getId();
        this.map = null;
    }

    public void editSlot(int slot, boolean rightClick, boolean shiftClick) {
        if (slot < 0) {
            this.setItem(slot);
            return;
        }
        if (this.hasItemAtSlot(slot)) {
            if (rightClick) {
                if (shiftClick) {
                    this.copyToClipboard(slot);
                } else {
                    this.editItem(slot);
                }
            } else {
                if (shiftClick) {
                    this.updateItem(slot, null);
                    //this.owner.sendErrorMessage("You must copy an item before pasting it! Use Shift + Right Click to copy.");
                } else {
                    this.setItem(slot);
                }
            }
        } else {
            if (rightClick) {
                if (shiftClick) {
                    this.updateItem(slot, this.clipboard);
                    //this.owner.sendErrorMessage("You must put an item in the slot before copying it!");
                } else {
                    //this.owner.sendErrorMessage("You must put an item in the slot before editing it!");
                    this.showPresetMenu();
                }
            } else {
                if (shiftClick) {
                    this.owner.sendSuccessMessage("Pasted clipboard.");
                    this.updateItem(slot, this.clipboard);
                } else {
                    this.setItem(slot);
                }
            }
        }
    }

    public void showPresetMenu() {
        List<Kit> duelsKits = Miscellaneous.getList(Kits.DUELS_CRYSTAL, Kits.DUELS_NETHERITE_POT, Kits.DUELS_DIAMOND_POT, Kits.DUELS_UHC, Kits.DUELS_AXE, Kits.DUELS_SWORD, Kits.SMP_KIT, Kits.DUELS_OP);

        SmartGUI presetSelect = new SmartGUI("Select Preset", 1, false);

        for (int i = 0; i < duelsKits.size(); i++) {
            Kit kit = duelsKits.get(i);
            presetSelect.add(i, kit.representative.itemStack, (player, b, b1) -> this.selectPreset(kit), GOLD + BOLD.toString() + kit.name.toUpperCase(), Miscellaneous.processList(s -> GRAY + s, kit.description.split(",")));
        }

        presetSelect.add(8, Material.BARRIER, (player, b, b1) -> this.back(), RED + "Back");

        this.owner.openGui(presetSelect);
        this.owner.playClickSound();
    }

    protected void selectPreset(Kit kit) {
        this.setFromKit(kit);

        if (kit == Kits.DUELS_AXE) {
            this.gameSettings.getSetting("rounds_to_win").setValueAndDefault(2);
            this.gameSettings.getSetting("block_drops").setValueAndDefault(false);
        }

        if (Miscellaneous.getList(Kits.DUELS_DIAMOND_POT, Kits.DUELS_NETHERITE_POT, Kits.DUELS_AXE, Kits.DUELS_SWORD).contains(kit)) {
            this.gameSettings.getSetting("break").setValueAndDefault(false);
        }

        if (kit == Kits.DUELS_SWORD) {
            this.gameSettings.getSetting("natural_regen").setValueAndDefault(false);
        }

        if (kit == Kits.DUELS_CRYSTAL) {
            this.gameSettings.getSetting("durability").setValueAndDefault(true);
            this.gameSettings.getSetting("rounds_to_win").setValueAndDefault(1);
            this.gameSettings.getSetting("time").setValueAndDefault(30);
        }

        this.back();
    }

    public void copyToClipboard(int slot) {
        this.clipboard = this.getItemStackOfSlot(slot);
        this.owner.sendSuccessMessage("Copied item. Use Shift + Left Click to paste.");
        this.owner.playSuccessSound();
    }

    public void setItem(int slot) {
        SlotType slotType = this.getTypeOfIndex(slot);
        switch (slotType) {
            case HELMET, CHESTPLATE, BOOTS, LEGGINGS -> {
                this.showItemSelect(slot, this.getMaterialsForType(slotType));
                return;
            }
        }

        this.showCategorySelect(slot);
    }

    public List<Material> getMaterialsForType(SlotType slotType) {
        return switch (slotType) {
            case HELMET -> Miscellaneous.getList(
                    Material.CARVED_PUMPKIN,
                    Material.TURTLE_HELMET,
                    Material.LEATHER_HELMET,
                    Material.CHAINMAIL_HELMET,
                    Material.GOLDEN_HELMET,
                    Material.IRON_HELMET,
                    Material.DIAMOND_HELMET,
                    Material.NETHERITE_HELMET);
            case CHESTPLATE -> Miscellaneous.getList(
                    Material.LEATHER_CHESTPLATE,
                    Material.CHAINMAIL_CHESTPLATE,
                    Material.GOLDEN_CHESTPLATE,
                    Material.IRON_CHESTPLATE,
                    Material.DIAMOND_CHESTPLATE,
                    Material.NETHERITE_CHESTPLATE,
                    Material.ELYTRA);
            case LEGGINGS -> Miscellaneous.getList(
                    Material.LEATHER_LEGGINGS,
                    Material.CHAINMAIL_LEGGINGS,
                    Material.GOLDEN_LEGGINGS,
                    Material.IRON_LEGGINGS,
                    Material.DIAMOND_LEGGINGS,
                    Material.NETHERITE_LEGGINGS);
            case BOOTS -> Miscellaneous.getList(
                    Material.LEATHER_BOOTS,
                    Material.CHAINMAIL_BOOTS,
                    Material.GOLDEN_BOOTS,
                    Material.IRON_BOOTS,
                    Material.DIAMOND_BOOTS,
                    Material.NETHERITE_BOOTS);
            default -> new ArrayList<>();
        };

    }

    public void editItem(int slot) {
        ItemStack itemStack = this.getItemStackOfSlot(slot);

        if (itemStack == null) return;

        SmartGUI smartGUI = new SmartGUI("Edit", 5, false);

        boolean isPotion = itemStack.getItemMeta() instanceof PotionMeta;
        if (isPotion) {
            smartGUI.add(10, Material.GLOWSTONE_DUST, (player, b, b2) -> this.changeAmplifier(slot), YELLOW + "Change Amplifier");
            smartGUI.add(12, Material.REDSTONE, (player, b, b2) -> this.changeDuration(slot), YELLOW + "Change Duration");
            smartGUI.add(14, Material.ARROW, (player, b, b2) -> this.changeCount(slot), YELLOW + "Change Count");
            smartGUI.add(16, Material.NAME_TAG, (player, b, b2) -> this.changeName(slot), YELLOW + "Change Name");
        } else {
            Material material = itemStack.getType();
            int maxCount = material.getMaxStackSize();
            if (getApplicableEnchantments(slot).isEmpty()) {
                if (maxCount == 0 || maxCount == 1) {
                    /*smartGUI.add(12, Material.ANVIL, (player, b, b2) -> */
                    this.changeDurability(slot)/*, YELLOW + "Change Durability")*/;
                } else {
                    /*smartGUI.add(12, Material.ARROW, (player, b, b2) -> */
                    this.changeCount(slot)/*, YELLOW + "Change Count")*/;
                }
                //smartGUI.add(14, Material.NAME_TAG, (player, b, b2) -> this.changeName(slot), YELLOW + "Change Name");
                return;
            } else {
                if (maxCount > 1) {
                    smartGUI.add(12, Material.ARROW, (player, b, b2) -> this.changeCount(slot), YELLOW + "Change Count");

                    smartGUI.add(14, Material.ENCHANTING_TABLE, (player, b, b2) -> this.enchantMenu(slot), YELLOW + "Change Enchantments");
                } else {
                    this.enchantMenu(slot);
                    return;
                }
                //smartGUI.add(15, Material.NAME_TAG, (player, b, b2) -> this.changeName(slot), YELLOW + "Change Name");
            }
        }

        smartGUI.add(31, Material.BARRIER, (player, b, b2) -> this.back(), RED + "Cancel");

        this.owner.openGui(smartGUI);
        this.owner.playClickSound();
    }

    private void changeFireworkCharge(int slot) {
        ItemStack itemStack = this.getItemStackOfSlot(slot);

        int max = this.getMaxAmplifier();

        FireworkMeta fireWorkMeta = (FireworkMeta) itemStack.getItemMeta();

        this.getStringInput("Charge (0 - " + max + ")", String.valueOf(fireWorkMeta.getEffectsSize()), s -> {
            try {
                int i = Integer.parseInt(s);

                i = Math.max(0, Math.min(max, i));

                ItemStack changed = itemStack.clone();
                int finalI1 = i;
                ItemUtilities.edit(changed, itemMeta -> {
                    FireworkMeta fireworkMeta = (FireworkMeta) itemMeta;
                    fireworkMeta.clearEffects();
                    for (int j = 0; j < finalI1; j++) {
                        fireworkMeta.addEffect(FireworkEffect.builder().withColor(Color.RED).withFlicker().build());
                    }
                });

                this.updateItem(slot, changed);
            } catch (Exception exception) {
                this.onInvalidInput();
            }

            this.editItem(slot);
        });
    }

    public void changeFireworkDuration(int slot) {
        ItemStack itemStack = this.getItemStackOfSlot(slot);

        int max = this.getMaxAmplifier();

        FireworkMeta fireWorkMeta = (FireworkMeta) itemStack.getItemMeta();

        this.getStringInput("Duration (1 - " + max + ")", String.valueOf(fireWorkMeta.getPower()), s -> {
            try {
                int i = Integer.parseInt(s);

                i = Math.max(1, Math.min(max, i));

                ItemStack changed = itemStack.clone();
                int finalI1 = i;
                ItemUtilities.edit(changed, itemMeta -> {
                    FireworkMeta fireworkMeta = (FireworkMeta) itemMeta;
                    fireworkMeta.setPower(finalI1);
                });

                this.updateItem(slot, changed);
            } catch (Exception exception) {
                this.onInvalidInput();
            }

            this.editItem(slot);
        });
    }

    public boolean isPotion(int slot) {
        return this.hasItemAtSlot(slot) && this.getItemStackOfSlot(slot).getItemMeta() instanceof PotionMeta;
    }

    public void changeCount(int slot) {
        ItemStack itemStack = this.getItemStackOfSlot(slot);
        Material material = itemStack.getType();
        int maxCount = material.getMaxStackSize();

        if (maxCount == 0 || maxCount == 1) {
            this.owner.playErrorSound();
            this.owner.sendErrorMessage("This item's size cannot be changed!");
            return;
        }

        int currentCount = itemStack.getAmount();

        boolean isFirework = itemStack.getItemMeta() instanceof FireworkMeta;

        SmartGUI countGUI = new SmartGUI("Count", isFirework ? 4 : 3, false);

        for (int i = 0; i < 6; i++) {
            int count;
            if (i == 0) {
                count = 1;
            } else if (i == 1) {
                count = maxCount / 8;
            } else {
                count = (int) (maxCount * ((i - 1) / 4.0));
            }
            final int finalCount = count;
            countGUI.add(i + 11, new ItemStack(material, finalCount), (player, b, b1) -> {
                ItemStack changed = itemStack.asQuantity(finalCount);
                this.updateItem(slot, changed);
                this.back();
            });
        }

        if (isFirework) {
            countGUI.add(30, Material.CLOCK, (player, b, b1) -> this.changeFireworkCharge(slot), YELLOW + "Change Charge");
            countGUI.add(32, Material.FIRE_CHARGE, (player, b, b1) -> this.changeFireworkDuration(slot), YELLOW + "Change Duration");
        }

        countGUI.add(10, Material.WRITABLE_BOOK, (player, b, b1) -> this.getStringInput("Count (1 - " + maxCount + ")", String.valueOf(currentCount), s -> {
                    try {
                        int i = Integer.parseInt(s);

                        i = Math.max(1, Math.min(maxCount, i));

                        ItemStack changed = itemStack.asQuantity(i);

                        this.updateItem(slot, changed);
                    } catch (Exception exception) {
                        this.onInvalidInput();
                    }

                    this.back();
                }),
                GOLD + "Custom Count");

        this.owner.openGui(countGUI);
        this.owner.playClickSound();
    }

    public void changeAmplifier(int slot) {
        ItemStack itemStack = this.getItemStackOfSlot(slot);

        int max = this.getMaxAmplifier();

        PotionMeta potionMeta = (PotionMeta) itemStack.getItemMeta();

        int duration;
        PotionEffectType type;

        if (potionMeta.hasCustomEffects()) {
            type = potionMeta.getCustomEffects().get(0).getType();
            duration = potionMeta.getCustomEffects().get(0).getDuration();
        } else {
            type = potionMeta.getBasePotionData().getType().getEffectType();
            duration = 20 * (potionMeta.getBasePotionData().isUpgraded() ? 7 : 3);
        }

        this.getStringInput("Amplifier (0 - " + max + ")", null, s -> {
            try {
                int i = Integer.parseInt(s);

                i = Math.max(0, Math.min(max, i));

                ItemStack changed = itemStack.clone();
                int finalI = i;
                ItemUtilities.edit(changed, itemMeta -> {
                    PotionMeta potionMeta1 = (PotionMeta) itemMeta;
                    potionMeta1.setBasePotionData(new PotionData(PotionType.WATER, false, false));
                    potionMeta1.addCustomEffect(new PotionEffect(type, duration, finalI), true);
                    String start = "";
                    switch (itemStack.getType()) {
                        case POTION:
                            start = "Potion";
                            break;
                        case SPLASH_POTION:
                            start = "Splash Potion";
                            break;
                        case LINGERING_POTION:
                            start = "Lingering Potion";
                            break;
                        case TIPPED_ARROW:
                            start = "Arrow";
                            break;
                    }

                    potionMeta1.displayName(Component.text(WHITE + start + " of " + KitItem.PotionKitItem.getPotionName(type)));
                });


                this.updateItem(slot, changed);
            } catch (Exception exception) {
                this.onInvalidInput();
            }

            this.editItem(slot);
        });
    }

    public int getMaxAmplifier() {
        if (this.owner.isAdministrator()) {
            return 255;
        }

        if (this.owner.isStaff()) {
            return 32;
        }

        if (this.owner.isInGroupOrHigher(Group.YOUTUBE)) {
            return 16;
        }

        return switch (this.owner.getHighestGroup()) {
            case CHAMPION -> 10;
            case TIERIII -> 8;
            case TIERII -> 6;
            case TIERI -> 4;
            default -> 2;
        };

    }

    public int getMaxDuration() {
        if (this.owner.isAdministrator()) {
            return 6000;
        }

        if (this.owner.isStaff()) {
            return 600;
        }

        if (this.owner.isInGroupOrHigher(Group.YOUTUBE)) {
            return 300;
        }

        switch (this.owner.getHighestGroup()) {
            case CHAMPION:
                return 180;
            case TIERIII:
                return 90;
            case TIERII:
                return 60;
            case TIERI:
                return 45;
        }

        return 30;
    }

    public void changeDurability(int slot) {
        ItemStack itemStack = this.getItemStackOfSlot(slot);
        Material material = itemStack.getType();

        if (!(itemStack.getItemMeta() instanceof Damageable) || material.getMaxDurability() == 0) {
            this.owner.playErrorSound();
            this.owner.sendErrorMessage("This item's durability cannot be changed!");
            return;
        }

        Damageable damageable = (Damageable) itemStack.getItemMeta();

        int maxDurability = material.getMaxDurability();
        int currentDurability = maxDurability - damageable.getDamage();

        this.getStringInput("Durability (1 - " + maxDurability + ")", String.valueOf(currentDurability), s -> {
            try {
                int i = Integer.parseInt(s);
                ItemStack changed = itemStack.clone();


                i = Math.max(1, Math.min(maxDurability, i));

                int finalI = i;
                ItemUtilities.edit(changed, itemMeta -> ((Damageable) itemMeta).setDamage(maxDurability - finalI));
                this.updateItem(slot, changed, false);
            } catch (Exception exception) {
                this.onInvalidInput();
            }

            this.enchantMenu(slot);
        });
    }

    public void changeDuration(int slot) {
        ItemStack itemStack = this.getItemStackOfSlot(slot);

        int max = this.getMaxDuration();

        PotionMeta potionMeta = (PotionMeta) itemStack.getItemMeta();

        int amplifier;
        PotionEffectType type;

        if (potionMeta.hasCustomEffects()) {
            type = potionMeta.getCustomEffects().get(0).getType();
            amplifier = potionMeta.getCustomEffects().get(0).getAmplifier();
        } else {
            type = potionMeta.getBasePotionData().getType().getEffectType();
            amplifier = 0;
        }

        this.getStringInput("Duration (1 - " + max + ")", null, s -> {
            try {
                int i = Integer.parseInt(s);

                i = Math.max(1, Math.min(max, i));

                ItemStack changed = itemStack.clone();
                int finalI = i;
                ItemUtilities.edit(changed, itemMeta -> {
                    PotionMeta potionMeta1 = (PotionMeta) itemMeta;
                    potionMeta1.setBasePotionData(new PotionData(PotionType.WATER, false, false));
                    potionMeta1.addCustomEffect(new PotionEffect(type, finalI * 20, amplifier), true);
                    String start = "";
                    switch (itemStack.getType()) {
                        case POTION:
                            start = "Potion";
                            break;
                        case SPLASH_POTION:
                            start = "Splash Potion";
                            break;
                        case LINGERING_POTION:
                            start = "Lingering Potion";
                            break;
                        case TIPPED_ARROW:
                            start = "Arrow";
                            break;
                    }

                    potionMeta1.displayName(Component.text(WHITE + start + " of " + KitItem.PotionKitItem.getPotionName(type)));
                });

                this.updateItem(slot, changed);
            } catch (Exception exception) {
                this.onInvalidInput();
            }

            this.editItem(slot);
        });
    }

    protected List<Enchantment> getApplicableEnchantments(int slot) {
        ItemStack itemStack = this.getItemStackOfSlot(slot);
        List<Enchantment> applicableEnchantments = Miscellaneous.getList(Enchantment.values());

        applicableEnchantments.removeIf(enchantment -> !enchantment.canEnchantItem(itemStack));

        return Miscellaneous.without(applicableEnchantments, Enchantment.BINDING_CURSE, Enchantment.VANISHING_CURSE);
    }

    public void enchantMenu(int slot) {

        List<Enchantment> applicableEnchantments = getApplicableEnchantments(slot);

        //Miscellaneous.addIfAbsent(applicableEnchantments, Enchantment.DAMAGE_ALL);
        //Miscellaneous.addIfAbsent(applicableEnchantments, Enchantment.KNOCKBACK);

        if (applicableEnchantments.isEmpty()) {
            this.owner.playErrorSound();
            this.owner.sendErrorMessage("This item cannot be enchanted.");
        } else {
            this.selectEnchantment(slot, applicableEnchantments);
        }
    }

    public void selectEnchantment(int slot, List<Enchantment> options) {
        SmartGUI smartGUI = new SmartGUI("Enchantments", 5, false);

        int itemIndex = 0;

        Map<Enchantment, Integer> kitToIndex = new HashMap<>();

        for (int row = 1; row < 4 && itemIndex < options.size(); row++) {
            for (int i = 1; i < 8 && itemIndex < options.size(); i++) {
                Enchantment enchantment = options.get(itemIndex++);
                final int currentIndex = row * 9 + i;
                kitToIndex.put(enchantment, currentIndex);
            }
        }

        ItemStack itemStack = this.getItemStackOfSlot(slot);

        for (Enchantment enchantment : kitToIndex.keySet()) {
            int index = kitToIndex.get(enchantment);

            ItemStack representative;
            if (itemStack.containsEnchantment(enchantment)) {
                if (enchantment.getMaxLevel() == 1) {

                    representative = ItemUtilities.enchanted(ItemUtilities.getItemStack(Material.BOOK, AQUA + Text.prettify(enchantment.getKey().getKey())));
                } else {
                    representative = ItemUtilities.enchanted(ItemUtilities.getItemStack(Material.BOOK, AQUA + Text.prettify(enchantment.getKey().getKey()), DARK_GRAY + "Level: " + GRAY + itemStack.getEnchantmentLevel(enchantment)));
                }
            } else {
                representative = ItemUtilities.getItemStack(Material.BOOK, AQUA + Text.prettify(enchantment.getKey().getKey()));
            }
            smartGUI.add(
                    index,
                    representative,
                    (player, b, b2) -> {
                        if (b) {
                            this.removeEnchantment(slot, enchantment);
                        } else {
                            this.enchant(slot, enchantment);
                        }
                    });
        }


        smartGUI.addImmutable(36, itemStack);

        smartGUI.add(
                0,
                Material.ARROW,
                (player, b, b2) -> this.back(),
                RED + "Back");
        smartGUI.add(38,
                Material.NAME_TAG,
                (player, b, b1) -> this.changeName(slot),
                GOLD + "Change Name");
        smartGUI.add(39,
                Material.ANVIL,
                (player, b, b1) -> this.changeDurability(slot),
                YELLOW + "Change Durability");

        smartGUI.add(40, Material.BEDROCK,
                (player, b, b1) -> this.toggleUnbreakable(slot),
                DARK_AQUA + "Toggle Unbreakable",
                itemStack.getItemMeta() != null && itemStack.getItemMeta().isUnbreakable() ? (GREEN + "ON") : (RED + "OFF"));

        smartGUI.add(
                41,
                Material.PINK_DYE,
                (player, b, b2) -> this.maxEnchants(slot),
                LIGHT_PURPLE + "Max Enchants");
        smartGUI.add(
                42,
                Material.NETHERITE_SCRAP,
                (player, b, b2) -> this.removeEnchants(slot),
                YELLOW + "Clear Enchants");
        this.owner.openGui(smartGUI);
        this.owner.playClickSound();
    }

    private void removeEnchantment(int slot, Enchantment enchantment) {
        ItemStack itemStack = this.getItemStackOfSlot(slot);

        if (itemStack.containsEnchantment(enchantment)) {
            itemStack.removeEnchantment(enchantment);
            this.updateItem(slot, itemStack, false);
            this.enchantMenu(slot);
        }
    }

    private void toggleUnbreakable(int slot) {
        ItemStack itemStack = this.getItemStackOfSlot(slot);
        if (itemStack.hasItemMeta() && itemStack.getItemMeta().isUnbreakable()) {
            this.updateItem(slot, ItemUtilities.edit(itemStack, itemMeta -> itemMeta.setUnbreakable(false)), false);
        } else {
            this.updateItem(slot, ItemUtilities.unbreakable(itemStack), false);
        }
        this.owner.playSuccessSound();
        this.enchantMenu(slot);
    }

    protected void maxEnchants(int slot) {
        List<Enchantment> applicableEnchantments = getApplicableEnchantments(slot);
        ItemStack itemStack = this.getItemStackOfSlot(slot);
        for (Enchantment enchantment : Miscellaneous.getList(Enchantment.PROTECTION_ENVIRONMENTAL, Enchantment.PIERCING, Enchantment.LOYALTY, Enchantment.DAMAGE_ALL, Enchantment.DEPTH_STRIDER)) {
            if (applicableEnchantments.contains(enchantment)) {
                itemStack.addEnchantment(enchantment, enchantment.getMaxLevel());
            }
        }
        for (Enchantment applicableEnchantment : applicableEnchantments) {
            boolean canEnchant = true;
            for (Enchantment enchantment : itemStack.getEnchantments().keySet()) {
                if (enchantment != applicableEnchantment && enchantment.conflictsWith(applicableEnchantment)) {
                    canEnchant = false;
                    break;
                }
            }
            if (canEnchant && applicableEnchantment.canEnchantItem(itemStack)) {
                itemStack.addEnchantment(applicableEnchantment, applicableEnchantment.getMaxLevel());
            }
        }
        this.updateItem(slot, itemStack, false);
        this.owner.playSuccessSound();
        this.enchantMenu(slot);
    }

    public void enchant(int slot, Enchantment enchantment) {
        ItemStack itemStack = this.getItemStackOfSlot(slot);

        SmartGUI smartGUI = new SmartGUI("Enchant", 1, false);

        int i = enchantment.getMaxLevel();

        if (i == 1) {
            ItemStack changed = itemStack.clone();
            ItemUtilities.edit(changed, itemMeta -> itemMeta.addEnchant(enchantment, 1, true));
            this.updateItem(slot, changed, false);
            this.enchantMenu(slot);
            return;
        }

        for (int j = 0; j < i; j++) {
            int finalJ = j;
            smartGUI.add(
                    j,
                    ItemUtilities.edit(Material.ENCHANTED_BOOK, itemMeta -> itemMeta.addEnchant(enchantment, finalJ + 1, true)),
                    (player, b, b2) -> {
                        ItemStack changed = itemStack.clone();
                        ItemUtilities.edit(changed, itemMeta -> itemMeta.addEnchant(enchantment, finalJ + 1, true));
                        this.updateItem(slot, changed, false);
                        this.enchantMenu(slot);
                    },
                    "");
        }

       /* int max = this.getMaxEnchantLevel(enchantment);

        this.getStringInput("Enchantment (1 - " + max + ")", "0", s -> {
            try {
                int i = Integer.parseInt(s);

                i = Math.max(1, Math.min(max, i));

                ItemStack changed = itemStack.clone();
                int finalI = i;
                ItemUtilities.edit(changed, itemMeta -> itemMeta.addEnchant(enchantment, finalI, true));

                this.updateItem(slot, changed);
            } catch (Exception exception) {
                this.onInvalidInput();
            }

            this.back();
        });*/

        smartGUI.add(8, Material.BARRIER, (player, b, b1) -> this.enchantMenu(slot), RED + "Back");

        smartGUI.fillEmptySpots();

        this.owner.openGui(smartGUI);
        this.owner.playClickSound();
    }

    public int getMaxEnchantLevel(Enchantment enchantment) {
        if (this.owner.isAdministrator()) {
            return 32767;
        }

        if (this.owner.isStaff()) {
            return 31;
        }

        if (this.owner.isInGroupOrHigher(Group.YOUTUBE)) {
            return 10;
        }

        return enchantment.getMaxLevel();
    }

    public void removeEnchants(int slot) {
        ItemStack itemStack = this.getItemStackOfSlot(slot);
        ItemStack changed = ItemUtilities.edit(itemStack, itemMeta -> {
            for (Enchantment enchantment : itemMeta.getEnchants().keySet()) {
                itemMeta.removeEnchant(enchantment);
            }
        });
        this.updateItem(slot, changed, false);
        this.enchantMenu(slot);
        return;
/*
        SmartGUI smartGUI = new SmartGUI("Clear Enchants?", 5, false);

        if (itemStack.getItemMeta().getEnchants().isEmpty()) {
            this.owner.playErrorSound();
            this.owner.sendErrorMessage("No enchantments to clear.");
            return;
        }

        smartGUI.add(13, itemStack, null);

        smartGUI.add(30, Material.EMERALD_BLOCK, (player, b, b2) -> {
            ItemStack changed = ItemUtilities.edit(itemStack, itemMeta -> {
                for (Enchantment enchantment : itemMeta.getEnchants().keySet()) {
                    itemMeta.removeEnchant(enchantment);
                }
            });
            this.updateItem(slot, changed, false);
            this.enchantMenu(slot);
        }, RED + "Confirm", YELLOW + "Remove all enchantments on item.");

        smartGUI.add(32, Material.BARRIER, (player, b, b2) -> this.enchantMenu(slot), YELLOW + "Cancel");

        this.owner.openGui(smartGUI);
        this.owner.playClickSound();
*/
    }

    public void changeName(int slot) {
        ItemStack itemStack = this.getItemStackOfSlot(slot);

        this.getStringInput("Change Name", null, s -> {
            try {
                if (!s.isEmpty()) {

                    if (this.owner.isInGroupOrHigher(Group.CHAMPION)) {
                        s = ChatColor.translateAlternateColorCodes('&', s);
                        s = SwearUtils.processMessageSwears(s, 4);
                    }

                    String finalS = s;
                    ItemStack changed = ItemUtilities.edit(itemStack.clone(), itemMeta -> itemMeta.displayName(Component.text(WHITE + finalS)));
                    this.updateItem(slot, changed, false);
                }
            } catch (Exception exception) {
                this.onInvalidInput();
            }
            this.back();
        });
    }

    public void showItemSelect(int slot, List<Material> options) {
        int rowsize = 9;
        int totalRows = (int) (Math.ceil(options.size() / (double) rowsize)) + 1;
        SmartGUI smartGUI = new SmartGUI("Select", totalRows, false);

        //smartGUI.add((totalRows - 1) * 9 + 3, Material.BLUE_STAINED_GLASS_PANE, (player, b, b2) -> this.updateItem(slot, null), GRAY + "None");
        smartGUI.add((totalRows - 1) * 9 + 4, Material.BARRIER, (player, b, b2) -> this.back(), RED + "Cancel");

        for (int i = 0; i < options.size(); i++) {
            Material material = options.get(i);
            if (material != null) {
                smartGUI.add(i, material, ((player, b, b1) -> this.updateItem(slot, new ItemStack(material))));
            }
        }

        /*if (options.size() % 2 == 0) {
            int startingIndex = 13 - options.size() / 2 + 1;
            for (int i = 0; i < options.size() - 1; i++) {
                Material material = options.get(i);
                smartGUI.add(startingIndex + i, material, (player, b, b2) -> this.updateItem(slot, new ItemStack(material)));
            }

            int i = options.size() - 1;
            Material material = options.get(i);
            smartGUI.add(22, material, (player, b, b2) -> this.updateItem(slot, new ItemStack(material)));
        } else {
            int startingIndex = 13 - options.size() / 2;
            for (int i = 0; i < options.size(); i++) {
                Material material = options.get(i);
                smartGUI.add(startingIndex + i, material, (player, b, b2) -> this.updateItem(slot, new ItemStack(material)));
            }
        }*/

        this.owner.openGui(smartGUI);
        this.owner.playClickSound();
    }

    public void showItemSelect(int slot, List<KitItem> options, ItemCategory itemCategory) {
        SmartGUI smartGUI = new SmartGUI("Select", 6, false);

        //smartGUI.add(47, Material.BLUE_STAINED_GLASS_PANE, (player, b, b2) -> this.updateItem(slot, null), GRAY + "None");
        smartGUI.add(49, Material.BARRIER, (player, b, b2) -> this.showCategorySelect(slot), RED + "Cancel");

/*
        int rowLength = switch (itemCategory) {
            case RANGED, ARMOR, WEAPONS, OTHER -> 6;
            case BLOCKS -> 7;
            case POTION -> 3;
            case FOOD -> 8;
        };

        for (int i = 0; i < options.size(); i++) {
            int index = this.calculatePosition(rowLength, i, options.size());
            KitItem kitItem = options.get(i);
            if (index >= 0 && index < 54) {
                smartGUI.add(index, kitItem.itemStack, (player, b, b2) -> this.updateItem(slot, kitItem.itemStack));
            } else {
                Main.warn("Invalid index! (i = %d, index = %d)!", i, index);
            }
        }
*/

        for (int index = 0; index < options.size(); index++) {
            KitItem kitItem = options.get(index);
            Material material = kitItem.itemStack.getType();
            if (kitItem.itemStack.getType() != Material.AIR) {
                if (index < 54) {
                    boolean maxStackSize = material != Material.COMPASS && slot != REPRESENTATIVE;
                    smartGUI.add(index, kitItem.itemStack, (player, b, b2) -> this.updateItem(slot, kitItem.itemStack.asQuantity(maxStackSize ? material.getMaxStackSize() : 1)));
                } else {
                    Main.warn("Invalid index! (i = %d, index = %d)!", index, index);
                }
            }
        }

        this.owner.openGui(smartGUI);
        this.owner.playClickSound();
    }

    public int calculatePosition(int rowLength, int index, int totalSize) {
        int rowNumber = index / rowLength;
        int positionInRow = index % rowLength;
        int rows = (int) Math.ceil((double) totalSize / (double) rowLength);
        int halfOfRowLength = rowLength / 2;
        int middleStart = 22 - (9 * (rows / 2)) + (9 * rowNumber);

        if (rowLength % 2 == 0) {
            int startPos = middleStart - halfOfRowLength;
            if ((positionInRow + 1) > halfOfRowLength && rowLength < 9) {
                return startPos + positionInRow + 1;
            }

            return startPos + positionInRow;
        }

        int startPos = middleStart - halfOfRowLength;

        return startPos + positionInRow;
    }

    public void updateItem(int slot, ItemStack itemStack) {
        this.updateItem(slot, itemStack, true);
    }

    public void updateItem(int slot, ItemStack itemStack, boolean back) {
        if (slot == REPRESENTATIVE) {
            if (itemStack == null) {
                itemStack = new ItemStack(Material.RED_BANNER);
            }
            itemStack.setAmount(1);
            this.representative = itemStack;
            if (back) {
                this.back();
            } else {
                this.fill();
            }
            this.owner.playSuccessSound();
            return;
        }
        if (slot < 18) {
            switch (this.getTypeOfIndex(slot)) {
                case HELMET:
                    this.helmet = itemStack;
                    break;
                case CHESTPLATE:
                    this.chestplate = itemStack;
                    break;
                case LEGGINGS:
                    this.leggings = itemStack;
                    break;
                case BOOTS:
                    this.boots = itemStack;
                    break;
                case OFF_HAND:
                    this.offHand = itemStack;
                    break;
                default:
                    this.representative = itemStack;
                    break;
            }
        } else {
            if (slot < 45) {
                int index;
                if (slot < 27) {
                    index = slot % 9 + 18;
                } else if (slot < 36) {
                    index = slot % 9 + 9;
                } else {
                    index = slot % 9;
                }

                this.inventory[index] = itemStack;
            } else {
                this.hotbar[slot % 9] = itemStack;
            }
        }

        if (back) {
            this.back();
        } else {
            this.fill();
            this.owner.playSuccessSound();
        }
    }

    public void showCategorySelect(int slot) {
        SmartGUI smartGUI = new SmartGUI("Categories", 5, false);

        List<ItemCategory> itemCategories = Miscellaneous.getList(ItemCategory.values());

        //smartGUI.add(30, Material.BLUE_STAINED_GLASS_PANE, (player, b, b2) -> this.updateItem(slot, null), GRAY + "None");
        smartGUI.add(31, Material.BARRIER, (player, b, b2) -> this.back(), RED + "Cancel");

        if (itemCategories.size() % 2 == 0) {
            int startingIndex = 13 - itemCategories.size() / 2 + 1;
            for (int i = 0; i < itemCategories.size() - 1; i++) {
                ItemCategory itemCategory = itemCategories.get(i);
                smartGUI.add(startingIndex + i, itemCategory.representative, (player, b, b2) -> this.showItemCategorySelect(itemCategory, slot), AQUA + itemCategory.name);
            }

            int i = itemCategories.size() - 1;
            ItemCategory itemCategory = itemCategories.get(i);
            smartGUI.add(22, itemCategory.representative, (player, b, b2) -> this.showItemCategorySelect(itemCategory, slot), AQUA + itemCategory.name);
        } else {
            int startingIndex = 13 - itemCategories.size() / 2;
            for (int i = 0; i < itemCategories.size(); i++) {
                ItemCategory itemCategory = itemCategories.get(i);
                smartGUI.add(startingIndex + i, itemCategory.representative, (player, b, b2) -> this.showItemCategorySelect(itemCategory, slot), AQUA + itemCategory.name);
            }
        }

        this.owner.openGui(smartGUI);
        this.owner.playClickSound();
    }

    public void showItemCategorySelect(ItemCategory itemCategory, int slot) {
        if (itemCategory == ItemCategory.POTION) {
            this.showPotionSelect(slot);
        } else {
            List<KitItem> options = this.getKitItemsForCategory(itemCategory);

            this.showItemSelect(slot, options, itemCategory);
        }
    }

    public static List<KitItem> getKitItemsForCategory(ItemCategory itemCategory) {
        switch (itemCategory) {
            case WEAPONS:
                return turnIntoKitItems(Material.WOODEN_SWORD, Material.STONE_SWORD, Material.IRON_SWORD, Material.GOLDEN_SWORD, Material.DIAMOND_SWORD, Material.NETHERITE_SWORD, Material.TRIDENT, Material.TOTEM_OF_UNDYING, Material.ENDER_PEARL, Material.WOODEN_AXE, Material.STONE_AXE, Material.IRON_AXE, Material.GOLDEN_AXE, Material.DIAMOND_AXE, Material.NETHERITE_AXE, Material.OBSIDIAN, Material.END_CRYSTAL, Material.WATER_BUCKET, Material.WOODEN_PICKAXE, Material.STONE_PICKAXE, Material.IRON_PICKAXE, Material.GOLDEN_PICKAXE, Material.DIAMOND_PICKAXE, Material.NETHERITE_PICKAXE, Material.RESPAWN_ANCHOR, Material.GLOWSTONE, Material.LAVA_BUCKET, Material.WOODEN_SHOVEL, Material.STONE_SHOVEL, Material.IRON_SHOVEL, Material.GOLDEN_SHOVEL, Material.DIAMOND_SHOVEL, Material.NETHERITE_SHOVEL, Material.RAIL, Material.TNT_MINECART, Material.FLINT_AND_STEEL, Material.WOODEN_HOE, Material.STONE_HOE, Material.IRON_HOE, Material.GOLDEN_HOE, Material.DIAMOND_HOE, Material.NETHERITE_HOE, Material.BOW, Material.CROSSBOW, Material.FISHING_ROD);
            case ARMOR:
                return turnIntoKitItems(Material.LEATHER_HELMET, Material.CHAINMAIL_HELMET, Material.GOLDEN_HELMET, Material.IRON_HELMET, Material.DIAMOND_HELMET, Material.NETHERITE_HELMET, Material.TURTLE_HELMET, Material.TOTEM_OF_UNDYING, Material.AIR, Material.LEATHER_CHESTPLATE, Material.CHAINMAIL_CHESTPLATE, Material.GOLDEN_CHESTPLATE, Material.IRON_CHESTPLATE, Material.DIAMOND_CHESTPLATE, Material.NETHERITE_CHESTPLATE, Material.ELYTRA, Material.AIR, Material.AIR, Material.LEATHER_LEGGINGS, Material.CHAINMAIL_LEGGINGS, Material.GOLDEN_LEGGINGS, Material.IRON_LEGGINGS, Material.DIAMOND_LEGGINGS, Material.NETHERITE_LEGGINGS, Material.SHIELD, Material.AIR, Material.AIR, Material.LEATHER_BOOTS, Material.CHAINMAIL_BOOTS, Material.GOLDEN_BOOTS, Material.IRON_BOOTS, Material.DIAMOND_BOOTS, Material.NETHERITE_BOOTS, Material.CARVED_PUMPKIN);
            case RANGED:
                List<KitItem> items = Miscellaneous.getList(turnIntoKitItems(Material.BOW, Material.CROSSBOW, Material.ARROW));
                for (PotionType value : Miscellaneous.without(Miscellaneous.getList(PotionType.values()), PotionType.UNCRAFTABLE, PotionType.WATER, PotionType.MUNDANE, PotionType.THICK, PotionType.AWKWARD, PotionType.NIGHT_VISION, PotionType.JUMP, PotionType.INSTANT_HEAL, PotionType.LUCK)) {
                    KitItem.ArrowKitItem arrowKitItem = new KitItem.ArrowKitItem(value, false, false, 1, -1);
                    items.add(arrowKitItem);
                    if (value.isUpgradeable()) {
                        KitItem.ArrowKitItem upgraded = new KitItem.ArrowKitItem(value, false, true, 1, -1);
                        items.add(upgraded);
                    }
                    if (value.isExtendable()) {
                        KitItem.ArrowKitItem extended = new KitItem.ArrowKitItem(value, true, false, 1, -1);
                        items.add(extended);
                    }
                }
                items.add(new KitItem(Material.SPECTRAL_ARROW));
                items.add(new KitItem(Material.FIREWORK_ROCKET));
                return items;
            case BLOCKS:
                return turnIntoKitItems(Material.OBSIDIAN, Material.OAK_LOG, Material.OAK_PLANKS, Material.DIRT, Material.COBBLESTONE, Material.DEEPSLATE, Material.END_STONE, Material.NETHERRACK, Material.GRAVEL, Material.SAND, Material.HAY_BLOCK, Material.MAGMA_BLOCK, Material.ICE, Material.SOUL_SAND, Material.SOUL_SOIL, Material.GRASS_BLOCK, Material.STONE, Material.WHITE_WOOL, Material.GLASS, Material.SCAFFOLDING, Material.TNT, Material.SLIME_BLOCK, Material.PUMPKIN, Material.MELON, Material.BONE_BLOCK, Material.SPONGE, Material.WET_SPONGE, Material.OAK_DOOR, Material.IRON_DOOR, Material.OAK_TRAPDOOR, Material.IRON_TRAPDOOR, Material.POINTED_DRIPSTONE, Material.HONEY_BLOCK, Material.HONEYCOMB_BLOCK, Material.NOTE_BLOCK, Material.JUKEBOX, Material.COAL_BLOCK, Material.IRON_BLOCK, Material.GOLD_BLOCK, Material.REDSTONE_BLOCK, Material.EMERALD_BLOCK, Material.LAPIS_BLOCK, Material.DIAMOND_BLOCK, Material.NETHERITE_BLOCK, Material.CRYING_OBSIDIAN);
            case FOOD:
                return turnIntoKitItems(Material.APPLE, Material.GOLDEN_APPLE, Material.ENCHANTED_GOLDEN_APPLE, Material.MELON, Material.SWEET_BERRIES, Material.GLOW_BERRIES, Material.CHORUS_FRUIT, Material.CARROT, Material.GOLDEN_CARROT, Material.POTATO, Material.BAKED_POTATO, Material.POISONOUS_POTATO, Material.BEETROOT, Material.DRIED_KELP, Material.BEEF, Material.COOKED_BEEF, Material.PORKCHOP, Material.COOKED_PORKCHOP, Material.MUTTON, Material.COOKED_MUTTON, Material.CHICKEN, Material.COOKED_CHICKEN, Material.RABBIT, Material.COOKED_RABBIT, Material.COD, Material.COOKED_COD, Material.SALMON, Material.COOKED_SALMON, Material.TROPICAL_FISH, Material.PUFFERFISH_BUCKET, Material.BREAD, Material.COOKIE, Material.CAKE, Material.PUMPKIN_PIE, Material.ROTTEN_FLESH, Material.SPIDER_EYE, Material.MUSHROOM_STEW, Material.BOWL, Material.RABBIT_STEW, Material.SUSPICIOUS_STEW, Material.HONEY_BOTTLE);
            case OTHER:
                items = new ArrayList<>();
                items.add(new KitItem(ItemUtilities.getItemStack(Material.COMPASS, RED + "Tracker")));
                items.addAll(turnIntoKitItems(Material.SHEARS, Material.EXPERIENCE_BOTTLE, Material.SNOWBALL, Material.RED_BED, Material.OAK_BOAT, Material.LEAD, Material.TOTEM_OF_UNDYING, Material.END_CRYSTAL, Material.BUCKET, Material.WATER_BUCKET, Material.LAVA_BUCKET, Material.MILK_BUCKET, Material.PUFFERFISH_BUCKET, Material.AXOLOTL_BUCKET, Material.POWDER_SNOW_BUCKET, Material.EGG, Material.FIREWORK_ROCKET, Material.RAIL, Material.POWERED_RAIL, Material.DETECTOR_RAIL, Material.ACTIVATOR_RAIL, Material.MINECART, Material.TNT_MINECART, Material.TNT, Material.COMPARATOR, Material.REPEATER, Material.REDSTONE, Material.REDSTONE_TORCH, Material.STONE_BUTTON, Material.LEVER, Material.STONE_PRESSURE_PLATE, Material.DROPPER, Material.DISPENSER, Material.TRIPWIRE_HOOK, Material.STRING, Material.HORSE_SPAWN_EGG, Material.MULE_SPAWN_EGG, Material.DONKEY_SPAWN_EGG, Material.PIG_SPAWN_EGG, Material.LEATHER_HORSE_ARMOR, Material.IRON_HORSE_ARMOR, Material.GOLDEN_HORSE_ARMOR, Material.DIAMOND_HORSE_ARMOR, Material.CARROT_ON_A_STICK));
                return items;
        }

        return new ArrayList<>();
    }

    public void showPotionSelect(int slot) {
        List<KitItem.PotionKitItem> options = new ArrayList<>();

        for (PotionEffectType value : PotionEffectType.values()) {
            if (PotionType.getByEffect(value) != null) {
                options.add(new KitItem.PotionKitItem(value, Material.POTION, false, false, -1));
            }
        }

        SmartGUI smartGUI = new SmartGUI("Select", 6, false);

        smartGUI.add(40, Material.BARRIER, (player, b, b2) -> this.showCategorySelect(slot), RED + "Cancel");

        int rowLength = 7;

        for (int i = 0; i < options.size(); i++) {
            // i = 7
            int row = i / rowLength; // 1
            int positionInRow = i % rowLength; // 0
            int remainingAfterLength = options.size() % rowLength; // 1
            int reduce = (i + 1) < (9 * options.size() / rowLength) ? rowLength / 2 : ((remainingAfterLength == 0 ? rowLength : remainingAfterLength) / 2);
            // 15 - 7 > 7 + 1 ? 7 / 2 : ((1 == 1 ? 7 : remainingAfterLength) / 2)
            int startingPositionInRow = 13 + (9 * row) - reduce;
            int index = startingPositionInRow + positionInRow;

            KitItem.PotionKitItem potionKitItem = options.get(i);
            if (index < 54) {
                PotionMeta potionMeta = (PotionMeta) potionKitItem.itemStack.getItemMeta();
                if (potionMeta.getCustomEffects().isEmpty()) {
                    smartGUI.add(index, potionKitItem.itemStack, (player, b, b2) -> this.showPotionSelect(slot, ((PotionMeta) potionKitItem.itemStack.getItemMeta()).getBasePotionData().getType().getEffectType()));
                } else {
                    smartGUI.add(index, potionKitItem.itemStack, (player, b, b2) -> this.showPotionSelect(slot, ((PotionMeta) potionKitItem.itemStack.getItemMeta()).getCustomEffects().get(0).getType()));
                }
            } else {
                Main.warn("Invalid index! (i = %d; index = %d)", i, index);
            }
        }

        this.owner.openGui(smartGUI);
        this.owner.playClickSound();
    }

    public void showPotionSelect(int slot, PotionEffectType type) {
        List<KitItem> options = new ArrayList<>();

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 2; j++) {
                for (int k = 0; k < 2; k++) {
                    boolean extended = j == 1;
                    boolean upgraded = k == 1;
                    Material material = switch (i) {
                        case 0 -> Material.POTION;
                        case 1 -> Material.SPLASH_POTION;
                        default -> Material.LINGERING_POTION;
                    };
                    PotionType potionType = PotionType.getByEffect(type);
                    if (potionType != null) {
                        if (!((!potionType.isExtendable() && extended) || (!potionType.isUpgradeable() && upgraded) || (upgraded && extended))) {
                            options.add(new KitItem.PotionKitItem(type, material, extended, upgraded, -1));
                        }
                    } else {
                        Main.error("Potion Type is null for %s", type);
                    }
                }
            }
        }

        this.showItemSelect(slot, options, ItemCategory.POTION);
    }

    public static List<KitItem> turnIntoKitItems(Material... materials) {
        return Miscellaneous.processList(Miscellaneous.getList(materials), KitItem::new);
    }

    public boolean hasItemAtSlot(int index) {
        ItemStack itemStack = this.getItemStackOfSlot(index);
        return itemStack != null && !Text.toComparable(itemStack.getType()).contains("pane");
    }

    public ItemStack getItemStackOfSlot(int index) {
        return this.gui.getItem(index);
    }

    public void back() {
        this.fill();

        this.owner.openGui(this);
        this.owner.playClickSound();
    }

    public SlotType getTypeOfIndex(int index) {
        switch (index) {
            case HELMET:
                return SlotType.HELMET;
            case CHESTPLATE:
                return SlotType.CHESTPLATE;
            case LEGGINGS:
                return SlotType.LEGGINGS;
            case BOOTS:
                return SlotType.BOOTS;
            case OFF_HAND:
                return SlotType.OFF_HAND;
            case REPRESENTATIVE:
                return SlotType.ANY;
        }

        return SlotType.ANY;
    }

    public void showLabelMenu() {
        SmartGUI smartGUI = new SmartGUI("Label", 1, false);

        smartGUI.add(2, Material.NAME_TAG, (player, b, b2) -> this.editName(), AQUA + "Change Name", GRAY + this.name);
        smartGUI.add(4, Material.BARRIER, (player, b, b1) -> this.back(), RED + "Back");
        smartGUI.add(6, this.representative.clone(), (player, b, b2) -> this.editSlot(REPRESENTATIVE, b, b2), RED + "Kit Icon");

        smartGUI.fillEmptySpots();

        this.owner.openGui(smartGUI);
        this.owner.playClickSound();
    }

    public void editName() {
        this.getStringInput("Kit Name", this.name, s -> {

            boolean isAcceptable = !s.isEmpty() && SwearUtils.canSendMessageAtLevel(s, ChatFilter.HIGH);

            if (this.owner.isInGroupOrHigher(Group.CHAMPION)) {
                s = ChatColor.translateAlternateColorCodes('&', s);
            }

            if (isAcceptable) {
                this.name = s;
                this.owner.playSuccessSound();
            } else {
                this.onInvalidInput();
            }

            this.back();
        });
    }

    public void onInvalidInput() {
        this.owner.playErrorSound();
        this.owner.sendErrorMessage("Invalid input!");
    }

    public void save() {
        if (this.deleted || this.shared) {
            return;
        }
        if (this.name.equals(DEFAULT_NAME)) {
            this.editName();
            return;
        }

        if (this.hotbar[0] != null && (this.representative == null || this.representative.getType() == Material.RED_BANNER)) {
            this.representative = this.hotbar[0].clone();
            this.representative.setAmount(1);
        }

        CustomDuelsKit kit = this.getAsKit();
        this.owner.saveCustomKit(kit, originalId);
    }

    public CustomDuelsKit getAsKit() {
        List<KitItem> items = new ArrayList<>();
        for (int i = 0; i < this.hotbar.length; i++) {
            ItemStack itemStack = this.hotbar[i];
            if (itemStack != null) {
                items.add(new KitItem(itemStack, i));
            }
        }

        for (int i = 0; i < this.inventory.length; i++) {
            ItemStack itemStack = this.inventory[i];
            if (itemStack != null) {
                items.add(new KitItem(itemStack, i + 9));
            }
        }

        if (this.offHand != null) {
            items.add(new KitItem(this.offHand, 40));
        }

        KitItem helmet = this.helmet == null ? null : new KitItem(this.helmet);
        KitItem chestplate = this.chestplate == null ? null : new KitItem(this.chestplate);
        KitItem leggings = this.leggings == null ? null : new KitItem(this.leggings);
        KitItem boots = this.boots == null ? null : new KitItem(this.boots);

        if (this.representative == null) {
            this.representative = new ItemStack(Material.RED_BANNER);
        }

        return new CustomDuelsKit(
                this.id,
                this.owner.getUUID(),
                new Kit(this.name, "Custom Kit.", false, new KitItem(this.representative), helmet, chestplate, leggings, boots, items, false),
                this.gameSettings.toJson(),
                this.getMaps(),
                this.publicCustomMap,
                shared,
                0,
                this.creators,
                0);
    }

    protected List<String> getMaps() {
        if (this.map == null) {
            if (this.customMap != null) {
                return Miscellaneous.getList(customMap);
            }
            return Miscellaneous.getList(DuelMap.ARENA.getCodeName());
        }

        return Miscellaneous.getList(this.map.getCodeName());
    }

    public void reset() {
        this.setFromOriginal();
        this.back();
    }

    public void cancel() {
        this.owner.openDuelsCustomKitsListGui(1);
    }

    public void delete() {
        SmartGUI smartGUI = new SmartGUI("Delete?", 3, false);
        smartGUI.add(12, Material.EMERALD_BLOCK, (player, b, b2) -> {
            this.owner.deleteKit(this.originalId, this.id);
            this.deleted = true;
        }, RED + "Confirm", YELLOW + "Permanently delete this custom kit.");

        smartGUI.add(14, Material.BARRIER, (player, b, b2) -> this.back(), YELLOW + "Cancel");

        this.owner.openGui(smartGUI);
        this.owner.playClickSound();
    }

    public boolean hasOriginal() {
        return this.originalKit != null;
    }

    public void getStringInput(String title, String originalText, Consumer<String> process) {
        new TextGUI(this.owner.getPlayer(), title, originalText) {
            @Override
            public void process(ItemStack itemStack) {
                process.accept(itemStack.getItemMeta().getDisplayName());
            }

            @Override
            public void onClose() {
                EditCustomKitGUI.this.back();
            }
        }.openInventory();
    }

    @Override
    public void onClose() {
        this.save();
    }

    enum SlotType {
        HELMET,
        CHESTPLATE,
        LEGGINGS,
        BOOTS,
        OFF_HAND,
        ANY
    }

    enum ItemCategory {
        WEAPONS("Weapons & Tools", Material.IRON_SWORD),
        ARMOR("Armor", Material.DIAMOND_CHESTPLATE),
        RANGED("Ranged", Material.CROSSBOW),
        BLOCKS("Blocks", Material.OAK_PLANKS),
        FOOD("Foodstuffs", Material.GOLDEN_CARROT),
        POTION("Potions", Material.GLASS_BOTTLE),
        OTHER("Other", Material.DIAMOND_HORSE_ARMOR),
        ;

        final String name;
        final Material representative;

        ItemCategory(String name, Material representative) {
            this.name = name;
            this.representative = representative;
        }
    }
}