package tc.oc.commons.bukkit.item;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import org.bukkit.Material;
import org.bukkit.Skin;
import org.bukkit.SkullType;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.SkullMeta;
import tc.oc.commons.bukkit.configuration.ConfigUtils;
import tc.oc.commons.bukkit.inventory.Slot;
import tc.oc.commons.bukkit.util.NMSHacks;
import tc.oc.commons.core.util.MapUtils;
import tc.oc.minecraft.api.configuration.InvalidConfigurationException;
import static tc.oc.commons.core.exception.LambdaExceptionUtils.rethrowSupplier;
/**
* Parses {@link ItemStack}s and {@link Skin}s from {@link ConfigurationSection}s
*/
public class ItemConfigurationParser {
private final ConfigurationSection root;
private final Map<String, Skin> skins = new HashMap<>();
public ItemConfigurationParser(ConfigurationSection root) {
this.root = root;
}
public Skin needSkin(String text) {
return MapUtils.computeIfAbsent(skins, text, t -> {
final ConfigurationSection skinSection = root.getConfigurationSection("skins");
if(skinSection != null) {
final String referenced = skinSection.getString(text);
if(referenced != null && !referenced.equals(text)) {
return needSkin(referenced);
}
}
return new Skin(text, null);
});
}
public <T extends Slot> T needSlotByPosition(ConfigurationSection section, @Nullable String key, @Nullable String value, Class<T> type) throws InvalidConfigurationException {
final String[] coords = ConfigUtils.needStringOrSectionName(section, key, value).split("\\s*,\\s*");
if(coords.length != 2) {
throw new InvalidConfigurationException(section, key, "Expected slot in \"column, row\" format");
}
final int column, row;
final T slot;
try {
slot = Slot.atPosition(type, column = Integer.parseInt(coords[0]), row = Integer.parseInt(coords[1]));
} catch(NumberFormatException e) {
throw new InvalidConfigurationException(section, key, e.getMessage());
}
if(slot == null) {
throw new InvalidConfigurationException(section, key, "No slot at column " + column + ", row " + row);
}
return slot;
}
public Material needItemType(ConfigurationSection section, String key) throws InvalidConfigurationException {
final Material item = NMSHacks.materialByKey(section.needString(key));
if(item == null) {
throw new InvalidConfigurationException(section, key, "Unknown item type '" + key + "'");
}
return item;
}
public void needSkull(ItemStack stack, ConfigurationSection section, String key) throws InvalidConfigurationException {
final ItemMeta meta = stack.getItemMeta();
if(!(meta instanceof SkullMeta)) {
throw new InvalidConfigurationException(section, key, "Item type " + NMSHacks.getKey(stack.getType()) + " cannot be skinned");
}
((SkullMeta) meta).setOwner("SkullOwner", UUID.randomUUID(), needSkin(section.needString(key)));
stack.setItemMeta(meta);
}
public ItemStack needSkull(ConfigurationSection section, String key) throws InvalidConfigurationException {
final ItemStack stack = new ItemStack(Material.SKULL_ITEM);
stack.setDurability((short) SkullType.PLAYER.ordinal());
needSkull(stack, section, key);
return stack;
}
public ItemStack getItem(ConfigurationSection section, String key, Supplier<ItemStack> def) throws InvalidConfigurationException {
if(section.isString(key)) {
return new ItemStack(needItemType(section, key));
}
if(!section.isConfigurationSection(key)) {
return def.get();
}
final ConfigurationSection itemSection = section.needSection(key);
if(itemSection.isString("skull")) {
return needSkull(itemSection, "skull");
}
final Material material = needItemType(itemSection, "id");
final int damage = itemSection.getInt("damage", 0);
if(damage < Short.MIN_VALUE || damage > Short.MAX_VALUE) {
throw new InvalidConfigurationException(itemSection, "damage", "Item damage out of range");
}
final ItemStack stack = new ItemStack(material, 1, (short) damage);
if(itemSection.isString("skin")) {
needSkull(stack, itemSection, "skin");
}
return stack;
}
public ItemStack needItem(ConfigurationSection section, String key) throws InvalidConfigurationException {
return getItem(section, key, rethrowSupplier(() -> {
throw new InvalidConfigurationException("Missing required item " + key);
}));
}
}