package net.obnoxint.mcdev.util;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.MaterialData;
/**
* <p>
* A serializable and immutable wrapper for org.bukkit.inventory.ItemStack objects.
* </p>
*/
public class SerializableItemStack implements Serializable {
private static final long serialVersionUID = 6251309292346486453L;
/**
* Creates a new instance based on the given string. The general contract of this method is that if the output of the {@link #toString()} is passed it will result in a clone.
*
* @param string the string.
* @return a new instance or null if string could not be parsed.
*/
public static SerializableItemStack fromString(final String string) {
SerializableItemStack r = null;
final String[] s = string.split(",");
if (s.length == 4 || s.length == 5) {
try {
final int type = Integer.parseInt(s[0]);
final int amount = Integer.parseInt(s[1]);
final short durability = Short.parseShort(s[2]);
final byte data = Byte.parseByte(s[3]);
final Map<Integer, Integer> enchantments = new HashMap<>();
if (s.length == 5) {
final String[] es = s[4].substring(1, s[4].length() - 1).split(";");
for (int i = 0; i < es.length; i++) {
final String[] e = es[i].split("=");
if (e.length == 2) {
enchantments.put(Integer.parseInt(e[0]), Integer.parseInt(e[1]));
}
}
}
r = new SerializableItemStack(type, amount, durability, data, enchantments);
} catch (final NumberFormatException e) {}
}
return r;
}
private static Map<Integer, Integer> getEnchantmentsMap(final Map<Enchantment, Integer> map) {
final Map<Integer, Integer> r = new HashMap<>();
for (final Enchantment e : map.keySet()) {
r.put(e.getId(), map.get(e));
}
return r;
}
private final int type;
private final int amount;
private final short durability;
private final byte data;
private final Map<Integer, Integer> enchantments;
/**
* Creates a new instance based on the given ItemStack.
*
* @param itemStack the ItemStack.
*/
public SerializableItemStack(final ItemStack itemStack) {
this(itemStack.getTypeId(),
itemStack.getAmount(),
itemStack.getDurability(),
itemStack.getData().getData(),
getEnchantmentsMap(itemStack.getEnchantments()));
}
private SerializableItemStack(final int type, final int amount, final short durability, final byte data, final Map<Integer, Integer> enchantments) {
this.type = type;
this.amount = amount;
this.durability = durability;
this.data = data;
this.enchantments = enchantments;
}
/**
* <p>
* The following types can be used to compare the instance with the passed value:
* </p>
* <ul>
* <li><b>SerializableItemstack:</b> All values (including enchantments) will be compared.</li>
* <li><b>Material:</b> Only the material id will be compared.</li>
* <li><b>Itemstack:</b> All values (including enchantments) will be compared by internally creating a new instance of SerializableItemStack based on the given ItemStack.</li>
* <li><b>String:</b> All values (including enchantments) will be compared by internally creating a new instance of SerializableItemStack. The internally used instance will be
* created through the {@link #fromString(String)} method.</li>
* </ul>
*
* @return true if the passed value is equal to this instance.
*/
@Override
public boolean equals(final Object obj) {
if (obj != null) {
if (obj instanceof SerializableItemStack) {
final SerializableItemStack o = (SerializableItemStack) obj;
return o.type == type && o.amount == amount && o.durability == durability && o.data == data && o.enchantments.equals(enchantments);
} else if (obj instanceof Material) {
return ((Material) obj).getId() == type;
} else if (obj instanceof ItemStack) {
final ItemStack o = (ItemStack) obj;
return equals(new SerializableItemStack(o));
} else if (obj instanceof String) {
return equals(fromString((String) obj));
}
}
return false;
}
/**
* @return the amount.
*/
public int getAmount() {
return amount;
}
/**
* @return the durability.
*/
public short getDurability() {
return durability;
}
/**
* @return a Map containing all enchantments (key) as well as their level (value).
*/
public Map<Enchantment, Integer> getEnchantments() {
final Map<Enchantment, Integer> r = new HashMap<>();
for (final int i : enchantments.keySet()) {
r.put(Enchantment.getById(i), enchantments.get(i));
}
return Collections.unmodifiableMap(r);
}
/**
* @return the Material.
*/
public Material getMaterial() {
return Material.getMaterial(type);
}
/**
* @return the MaterialData.
*/
public MaterialData getMaterialData() {
return new MaterialData(type, data);
}
/**
* @return the type.
*/
public int getType() {
return type;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + amount;
result = prime * result + data;
result = prime * result + durability;
result = prime * result + ((enchantments == null) ? 0 : enchantments.hashCode());
result = prime * result + type;
return result;
}
/**
* Creates a new ItemStack based on this instance. If this instance contains enchantments they will be added in an unsafe way.
*
* @return the ItemStack.
*/
public ItemStack toItemStack() {
final ItemStack r = new ItemStack(getMaterial(), amount, durability, data);
final Map<Enchantment, Integer> m = getEnchantments();
for (final Enchantment e : m.keySet()) {
r.addUnsafeEnchantment(e, m.get(e));
}
return r;
}
/**
* <p>
* Creates a String representing this instance in the following format:
* <p>
* <ul>
* <li><b>Without enchantments:</b> [type],[amount],[durability],[data]</li>
* <li><b>With enchantments:</b> [type],[amount],[durability],[data],{[enchantment1]=[level];[enchantment2]=[level]}</li>
* </ul>
*
* @return a String representing this instance.
*/
@Override
public String toString() {
final StringBuilder sb = new StringBuilder()
.append(type).append(",")
.append(amount).append(",")
.append(durability).append(",")
.append(data);
if (!enchantments.isEmpty()) {
sb.append(",{");
int c = 1;
for (final int i : enchantments.keySet()) {
sb.append(i).append("=").append(enchantments.get(i));
if (c != enchantments.size()) {
sb.append(";");
}
c++;
}
sb.append("}");
}
return sb.toString();
}
}