// Path: src/main/java/net/techcable/spudcompat/nbt/NBT.java
// public interface NBT {
//     public Tag getValue();
//     public void write(ByteBuf buf);
// 
// 
//     /**
//      * Calculate the length of the NBT, then return a lazy reading nbt-object.
//      * <p>This allows you to defer reading the nbt until later, often saving space and time.</p>
//      * <p>Caution: Some errors may not be found until later.</p>
//      *
//      * @param buf    the buf to read from
//      * @return a lazy-reading NBT object
//      */
//     public static NBT lazyRead(ByteBuf buf) {
//         return lazyRead(buf, NBTUtils.calculateNBTLength(buf));
//     }
// 
//     /**
//      * Return a NBT object that will read the given number of bytes from the buffer, and turn it into NBT only if/when needed.
//      * <p>This allows you to defer reading the nbt until later, saving space and time.</p>
//      * <p>Warning: Errors in the NBT will not be found until later.</p>
//      *
//      * @param buf    the buf to read from
//      * @param length the amount to read
//      * @return a lazy-reading NBT object
//      */
//     public static NBT lazyRead(ByteBuf buf, int length) {
//         return new LazyReadingNBT(buf.readBytes(length));
//     }
// 
//     /**
//      * Read the nbt from the given byte buf
//      *
//      * @param buf the buf to read the nbt from
//      * @return the nbt
//      */
//     @SneakyThrows(IOException.class) // ByteBufs don't throw IOExceptions
//     public static NBT read(ByteBuf buf) {
//         return read(new ByteBufInputStream(buf));
//     }
// 
//     /**
//      * Read the nbt from the given input stream
//      *
//      * @param in the stream to read the nbt from
//      * @return the nbt
//      */
//     public static NBT read(InputStream in) throws IOException {
//         return new SimpleNBT(NBTIO.readTag(new DataInputStream(in)));
//     }
// 
//     public static NBT create(Tag tag) {
//         return new SimpleNBT(tag);
//     }
// }


import lombok.*;
import lombok.experimental.*;
import java.util.Optional;
import com.google.common.base.Preconditions;
import io.netty.buffer.ByteBuf;
import net.techcable.spudcompat.nbt.NBT;

    private ItemStack(Material type, int amount, int data, Optional<NBT> nbt) {
        Preconditions.checkArgument(type != Material.AIR, "Air is not a valid type");
        Preconditions.checkArgument(data == (short) data, "Data %s doesn't fit into a short", data);
        this.type = Preconditions.checkNotNull(type, "Null type");
        this.amount = amount;
        this.data = (short) data;
        this.nbt = Preconditions.checkNotNull(nbt, "Null nbt optional");
    }

    public ItemStack(Material type, int amount, int data) {
        this(type, amount, data, Optional.empty());
    }


    public ItemStack(Material type, int amount, int data, NBT nbt) {
        this(type, amount, data, Optional.of(nbt));
    }

    public short getRawData() {
        return data;
    }

    public static Optional<ItemStack> read(ProtocolVersion version, ByteBuf buf) {
        short itemId = buf.readShort();
        if (itemId < 0) return Optional.empty();
        int amount = buf.readByte();
        short data = buf.readShort();
        Optional<NBT> nbt;
        if (buf.getByte(buf.readerIndex()) != 0) {
// Path: src/main/java/net/techcable/spudcompat/nbt/NBT.java
// public interface NBT {
//     public Tag getValue();
//     public void write(ByteBuf buf);
// 
// 
//     /**
//      * Calculate the length of the NBT, then return a lazy reading nbt-object.
//      * <p>This allows you to defer reading the nbt until later, often saving space and time.</p>
//      * <p>Caution: Some errors may not be found until later.</p>
//      *
//      * @param buf    the buf to read from
//      * @return a lazy-reading NBT object
//      */
//     public static NBT lazyRead(ByteBuf buf) {
//         return lazyRead(buf, NBTUtils.calculateNBTLength(buf));
//     }
// 
//     /**
//      * Return a NBT object that will read the given number of bytes from the buffer, and turn it into NBT only if/when needed.
//      * <p>This allows you to defer reading the nbt until later, saving space and time.</p>
//      * <p>Warning: Errors in the NBT will not be found until later.</p>
//      *
//      * @param buf    the buf to read from
//      * @param length the amount to read
//      * @return a lazy-reading NBT object
//      */
//     public static NBT lazyRead(ByteBuf buf, int length) {
//         return new LazyReadingNBT(buf.readBytes(length));
//     }
// 
//     /**
//      * Read the nbt from the given byte buf
//      *
//      * @param buf the buf to read the nbt from
//      * @return the nbt
//      */
//     @SneakyThrows(IOException.class) // ByteBufs don't throw IOExceptions
//     public static NBT read(ByteBuf buf) {
//         return read(new ByteBufInputStream(buf));
//     }
// 
//     /**
//      * Read the nbt from the given input stream
//      *
//      * @param in the stream to read the nbt from
//      * @return the nbt
//      */
//     public static NBT read(InputStream in) throws IOException {
//         return new SimpleNBT(NBTIO.readTag(new DataInputStream(in)));
//     }
// 
//     public static NBT create(Tag tag) {
//         return new SimpleNBT(tag);
//     }
// }


// Path: src/main/java/net/techcable/spudcompat/ItemStack.java
import lombok.*;
import lombok.experimental.*;
import java.util.Optional;
import com.google.common.base.Preconditions;
import io.netty.buffer.ByteBuf;
import net.techcable.spudcompat.nbt.NBT;


    private ItemStack(Material type, int amount, int data, Optional<NBT> nbt) {
        Preconditions.checkArgument(type != Material.AIR, "Air is not a valid type");
        Preconditions.checkArgument(data == (short) data, "Data %s doesn't fit into a short", data);
        this.type = Preconditions.checkNotNull(type, "Null type");
        this.amount = amount;
        this.data = (short) data;
        this.nbt = Preconditions.checkNotNull(nbt, "Null nbt optional");
    }

    public ItemStack(Material type, int amount, int data) {
        this(type, amount, data, Optional.empty());
    }


    public ItemStack(Material type, int amount, int data, NBT nbt) {
        this(type, amount, data, Optional.of(nbt));
    }

    public short getRawData() {
        return data;
    }

    public static Optional<ItemStack> read(ProtocolVersion version, ByteBuf buf) {
        short itemId = buf.readShort();
        if (itemId < 0) return Optional.empty();
        int amount = buf.readByte();
        short data = buf.readShort();
        Optional<NBT> nbt;
        if (buf.getByte(buf.readerIndex()) != 0) {
            nbt = Optional.of(NBT.lazyRead(buf));