package org.shininet.bukkit.itemrenamer;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack;
import org.shininet.bukkit.itemrenamer.api.RenamerSnapshot;
import org.shininet.bukkit.itemrenamer.meta.CompoundStore;
import org.shininet.bukkit.itemrenamer.utils.StackUtils;
import com.comphenix.protocol.wrappers.nbt.NbtBase;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.google.common.base.Objects;
public abstract class AbstractRenameProcessor {
/**
* An item stack serializer/deserializer.
*/
protected SerializeItemStack itemSerializer;
/**
* An item stack representing AIR.
*/
protected NbtCompound compoundAir;
/**
* Represents the key of the custom NBT tag that will store the original item stack.
*/
protected String compoundKey;
/**
* Construct a new rename processor.
* @param compoundKey - key of the custom NBT tag that will store the original item stack.
*/
public AbstractRenameProcessor(String compoundKey) {
this(RenameProcessor.createSerializer(), compoundKey);
}
/**
* Construct a new rename processor.
* @param itemSerializer - the item stack serializer/deserializer.
* @param compoundKey - key of the custom NBT tag that will store the original item stack.
*/
public AbstractRenameProcessor(SerializeItemStack itemSerializer, String compoundKey) {
super();
this.itemSerializer = itemSerializer;
this.compoundAir = itemSerializer.save(new ItemStack(Material.AIR, 0));
this.compoundKey = compoundKey;
}
/**
* Apply a player's associated rename rules to a given stack.
* <p>
* The rename rules are referenced by the world the player is in or by the player itself.
* @param player - the player.
* @param input - the item to rename.
* @param slotIndex - the slot index of the item we are changing.
* @return The processed item stack.
*/
public ItemStack process(Player player, ItemStack input, int offset) {
return process(player, player.getOpenInventory(), input, offset);
}
/**
* Apply a custom modification to the given item stacks.
* @param player - the player.
* @param InventoyView - the current inventory view.
* @param input - the item to rename.
* @param slotIndex - index of the item in the inventory view we want to rename.
* @return The processed item stack.
*/
public ItemStack process(Player player, InventoryView view, ItemStack input, int offset) {
ItemStack[] temporary = new ItemStack[] { input };
return process(player, view, temporary, offset)[0];
}
/**
* Apply a custom modification to the given item stacks.
* @param player - the recieving player.
* @param input - the item stack to process.
* @return The processed item stacks.
*/
public ItemStack[] process(Player player, ItemStack[] input) {
if (input != null) {
return process(player, player.getOpenInventory(), input, 0);
}
return null;
}
/**
* Apply a custom modification to the given item stacks.
* @param view - the current inventory view.
* @param input - the item to process.
* @param offset - the current offset.
* @return The processed item.
*/
public ItemStack[] process(Player player, InventoryView view, ItemStack[] input, int offset) {
final RenamerSnapshot snapshot = new RenamerSnapshot(input, view, offset);
final NbtCompound[] original = getCompounds(input);
// Just return it - for chaining
processSnapshot(player, snapshot);
// Save the original NBT tag
// Add a simple marker allowing us to restore the item stack
for (int i = 0; i < original.length; i++) {
ItemStack converted = snapshot.getSlot(i);
// Ensure that we are dealing with a CraftItemStack
if (isNotEmpty(converted)) {
converted = StackUtils.getCraftItemStack(converted);
} else {
if (!Objects.equal(original[i], compoundAir)) {
throw new IllegalStateException(
"Attempted to destroy an ItemStack at slot " + i + ": " + converted);
}
continue;
}
NbtCompound extra = snapshot.getCustomData(i, false);
NbtCompound tag = NbtFactory.asCompound(NbtFactory.fromItemTag(converted));
// Store extra NBT data
if (extra != null)
storeExtra(extra, tag, converted);
if (hasChanged(original[i], converted, tag))
converted = CompoundStore.getNativeStore(converted, compoundKey).saveCompound(original[i]);
input[i] = converted;
}
return input;
}
/**
* Retrieve the respective NBT compounds of each item stack in the array.
* @param input - the items.
* @return The compounds.
*/
private NbtCompound[] getCompounds(ItemStack[] input) {
NbtCompound[] original = new NbtCompound[input.length];
for (int i = 0; i < original.length; i++) {
if (input[i] != null) {
original[i] = itemSerializer.save(input[i]);
} else {
original[i] = compoundAir;
}
}
return original;
}
/**
* Apply a custom modification to a set of item stacks.
* @param player - the current player.
* @param snapshot - the items to modify,
* @return The modified items.
*/
protected abstract void processSnapshot(Player player, RenamerSnapshot snapshot);
/**
* Determine if a givne item stack is empty nor not.
* @param stack - the stack to test.
* @return TRUE if it is, FALSE otherwise.
*/
private boolean isNotEmpty(ItemStack stack) {
return stack != null && stack.getType() != Material.AIR;
}
/**
* Determine if a given item stack (serialized in a compound) has changed.
* @param savedStack - the serialized item stack.
* @param currentStack - the stack to compare to.
* @param currentTag - the current TAG compound.
* @return TRUE if it has changed, FALSE otherwise.
*/
protected boolean hasChanged(NbtCompound savedStack, ItemStack currentStack, NbtCompound currentTag) {
if (savedStack.getShort("id") != currentStack.getTypeId())
return true;
if (savedStack.getShort("damage") != currentStack.getDurability())
return true;
return !Objects.equal(savedStack.getObject("tag"), currentTag);
}
/**
* Merge the source compound into the destination tag, storing the result in the destination stack.
* @param source - the source compound.
* @param destinationTag - the destination tag.
* @param destinationStack - the destination stack.
*/
private void storeExtra(NbtCompound source, NbtCompound destinationTag, ItemStack destinationStack) {
// Overwrite parts of the NBT tag
for (NbtBase<?> base : source) {
destinationTag.put(base);
}
NbtFactory.setItemTag(destinationStack, destinationTag);
}
/**
* Undo a item rename, or leave as is.
* @param input - the stack to undo.
* @return TRUE if we removed the rename and lore, FALSE otherwise.
*/
public boolean unprocess(ItemStack input) {
if (input != null) {
// This will only be invoked for creative players
NbtCompound saved = CompoundStore.getNativeStore(input, compoundKey).loadCompound();
// See if there is something to restore
if (saved != null) {
itemSerializer.loadInto(input, saved, true);
return true;
}
}
return false;
}
}