package net.minecraft.network.protocol.game;

import net.minecraft.core.BlockPosition;
import net.minecraft.core.EnumDirection;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.syncher.DataWatcher;
import net.minecraft.world.entity.EntityTypes;
import net.minecraft.world.phys.Vec3D;
import com.bergerkiller.bukkit.common.internal.logic.ProtocolMath;

import org.bukkit.block.BlockFace;
import com.bergerkiller.bukkit.common.bases.IntVector3;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.PacketPlayOutSpawnEntityLivingHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.PacketPlayOutSpawnEntityHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.PacketPlayOutNamedEntitySpawnHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.PacketPlayOutSpawnEntityHandleHandle;
import com.bergerkiller.generated.net.minecraft.world.entity.EntityTypesHandle;

class PacketPlayOutSpawnEntity extends Packet {
#if version >= 1.20.5
    #bootstrap com.bergerkiller.bukkit.common.internal.CommonBootstrap.initServer();
#endif

#if version >= 1.17
    private int entityId:id;
    private optional UUID entityUUID:uuid;
  #if version >= 1.19
    private optional (EntityTypesHandle) EntityTypes<?> opt_entityType:type;
  #endif
    private optional int posX_1_8_8:###;
    private optional int posY_1_8_8:###;
    private optional int posZ_1_8_8:###;
    private optional double posX_1_10_2:x;
    private optional double posY_1_10_2:y;
    private optional double posZ_1_10_2:z;
  #if version >= 1.19
    private unknown byte xRot;
    private unknown byte yRot;
    private unknown byte yHeadRot;
  #else
    private unknown int xRot;
    private unknown int yRot;
  #endif
    private optional int opt_entityTypeId:###;

  #if version <= 1.18.2
    private optional (EntityTypesHandle) EntityTypes<?> opt_entityType:type;
  #endif
    private int extraData:data;
#elseif version >= 1.9
    private int entityId:a;
    private optional UUID entityUUID:b;
    private optional int posX_1_8_8:###;
    private optional int posY_1_8_8:###;
    private optional int posZ_1_8_8:###;
    private optional double posX_1_10_2:c;
    private optional double posY_1_10_2:d;
    private optional double posZ_1_10_2:e;
    private unknown int pitch_raw:i;
    private unknown int yaw_raw:j;

  #if version >= 1.14
    private optional int opt_entityTypeId:###;
    private optional (EntityTypesHandle) EntityTypes<?> opt_entityType:k;
  #else
    private optional int opt_entityTypeId:k;
    private optional (EntityTypesHandle) EntityTypes<?> opt_entityType:###;
  #endif

    private int extraData:l;
#else
    private int entityId:a;
    private optional UUID entityUUID:###;
    private optional int posX_1_8_8:b;
    private optional int posY_1_8_8:c;
    private optional int posZ_1_8_8:d;
    private optional double posX_1_10_2:###;
    private optional double posY_1_10_2:###;
    private optional double posZ_1_10_2:###;
    private unknown int pitch_raw:h;
    private unknown int yaw_raw:i;
    private optional int opt_entityTypeId:j;
    private optional (EntityTypesHandle) EntityTypes<?> opt_entityType:###;
    private int extraData:k;
#endif

#if version >= 1.21.9
    #require PacketPlayOutSpawnEntity private final Vec3D movement;
#elseif version >= 1.17
    #require PacketPlayOutSpawnEntity private int motX_raw:xa;
    #require PacketPlayOutSpawnEntity private int motY_raw:ya;
    #require PacketPlayOutSpawnEntity private int motZ_raw:za;
#elseif version >= 1.9
    #require PacketPlayOutSpawnEntity private int motX_raw:f;
    #require PacketPlayOutSpawnEntity private int motY_raw:g;
    #require PacketPlayOutSpawnEntity private int motZ_raw:h;
#else
    #require PacketPlayOutSpawnEntity private int motX_raw:e;
    #require PacketPlayOutSpawnEntity private int motY_raw:f;
    #require PacketPlayOutSpawnEntity private int motZ_raw:g;
#endif

    public static (PacketPlayOutSpawnEntityHandle) PacketPlayOutSpawnEntity createNew() {
#if version >= 1.20.5
        #require net.minecraft.network.protocol.game.PacketPlayOutSpawnEntity private PacketPlayOutSpawnEntity createSpawnPacket:<init>(net.minecraft.network.RegistryFriendlyByteBuf serializer);
        PacketPlayOutSpawnEntity packet = #createSpawnPacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
  #if version >= 1.21.9
        packet#movement = Vec3D.ZERO;
  #endif
        return packet;
#elseif version >= 1.17
        return new PacketPlayOutSpawnEntity(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#else
        return new PacketPlayOutSpawnEntity();
#endif
    }

#if version >= 1.19
    #require PacketPlayOutSpawnEntity private final byte xRot;
    #require PacketPlayOutSpawnEntity private final byte yRot;

    public float getYaw() {
        byte prot = instance#yRot;
        return ProtocolMath.deserializeRotation((int) prot);
    }
    public float getPitch() {
        byte prot = instance#xRot;
        return ProtocolMath.deserializeRotation((int) prot);
    }
    public void setYaw(float yaw) {
        byte prot = (byte) ProtocolMath.serializeRotation(yaw);
        instance#yRot = prot;
    }
    public void setPitch(float pitch) {
        byte prot = (byte) ProtocolMath.serializeRotation(pitch);
        instance#xRot = prot;
    }
#else
  #if version >= 1.17
    #require PacketPlayOutSpawnEntity private int xRot;
    #require PacketPlayOutSpawnEntity private int yRot;
  #elseif version >= 1.9
    #require PacketPlayOutSpawnEntity private int xRot:i;
    #require PacketPlayOutSpawnEntity private int yRot:j;
  #else
    #require PacketPlayOutSpawnEntity private int xRot:h;
    #require PacketPlayOutSpawnEntity private int yRot:i;
  #endif

    public float getYaw() {
        int prot = instance#yRot;
        return ProtocolMath.deserializeRotation(prot);
    }
    public float getPitch() {
        int prot = instance#xRot;
        return ProtocolMath.deserializeRotation(prot);
    }
    public void setYaw(float yaw) {
        int prot = ProtocolMath.serializeRotation(yaw);
        instance#yRot = prot;
    }
    public void setPitch(float pitch) {
        int prot = ProtocolMath.serializeRotation(pitch);
        instance#xRot = prot;
    }
#endif

#if version >= 1.21.9
    public (org.bukkit.util.Vector) Vec3D getMotVector:getMovement();

    public void setMotVector((org.bukkit.util.Vector) Vec3D motVector) {
        instance#movement = motVector;
    }

    public double getMotX() { return instance.getMovement().x; }
    public double getMotY() { return instance.getMovement().y; }
    public double getMotZ() { return instance.getMovement().z; }

    public void setMotX(double motX) {
        Vec3D movement = instance.getMovement();
        if (movement == null) movement = Vec3D.ZERO; // Bugfix for past use of newHandleNull()
        Vec3D updated = new Vec3D(motX, movement.y, movement.z);
        instance#movement = updated;
    }

    public void setMotY(double motY) {
        Vec3D movement = instance.getMovement();
        if (movement == null) movement = Vec3D.ZERO; // Bugfix for past use of newHandleNull()
        Vec3D updated = new Vec3D(movement.x, motY, movement.z);
        instance#movement = updated;
    }

    public void setMotZ(double motZ) {
        Vec3D movement = instance.getMovement();
        if (movement == null) movement = Vec3D.ZERO; // Bugfix for past use of newHandleNull()
        Vec3D updated = new Vec3D(movement.x, movement.y, motZ);
        instance#movement = updated;
    }
#else
    public (org.bukkit.util.Vector) Vec3D getMotVector() {
        int motX_raw = instance#motX_raw;
        int motY_raw = instance#motY_raw;
        int motZ_raw = instance#motZ_raw;
        return new Vec3D( com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motX_raw),
                          com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motY_raw),
                          com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motZ_raw) );
    }

    public void setMotVector((org.bukkit.util.Vector) Vec3D motVector) {
        int motX_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motVector.x);
        int motY_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motVector.y);
        int motZ_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motVector.z);
        instance#motX_raw = motX_raw;
        instance#motY_raw = motY_raw;
        instance#motZ_raw = motZ_raw;
    }

    public double getMotX() {
        int motX_raw = instance#motX_raw;
        return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motX_raw);
    }

    public double getMotY() {
        int motY_raw = instance#motY_raw;
        return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motY_raw);
    }

    public double getMotZ() {
        int motZ_raw = instance#motZ_raw;
        return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motZ_raw);
    }

    public void setMotX(double motX) {
        int motX_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motX);
        instance#motX_raw = motX_raw;
    }

    public void setMotY(double motY) {
        int motY_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motY);
        instance#motY_raw = motY_raw;
    }

    public void setMotZ(double motZ) {
        int motZ_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motZ);
        instance#motZ_raw = motZ_raw;
    }
#endif

    <code>
    @Override
    public com.bergerkiller.bukkit.common.protocol.PacketType getPacketType() {
        return com.bergerkiller.bukkit.common.protocol.PacketType.OUT_ENTITY_SPAWN;
    }

    public void setEntityUUID(UUID uuid) {
        if (T.entityUUID.isAvailable()) {
            T.entityUUID.set(getRaw(), uuid);
        }
    }

    public static boolean isEntityTypeSupported(org.bukkit.entity.EntityType type) {
        return isCommonEntityTypeSupported(com.bergerkiller.bukkit.common.entity.CommonEntityType.byEntityType(type));
    }

    public static boolean isCommonEntityTypeSupported(com.bergerkiller.bukkit.common.entity.CommonEntityType commonEntityType) {
        if (commonEntityType == null) {
            return false;
        } else if (T.opt_entityTypeId.isAvailable()) {
            return (commonEntityType.objectTypeId != -1);
        } else {
            return (commonEntityType.nmsEntityType != null);
        }
    }

    public void setCommonEntityType(com.bergerkiller.bukkit.common.entity.CommonEntityType commonEntityType) {
        if (commonEntityType == null) {
            throw new IllegalArgumentException("Input CommonEntityType is null");
        }
        if (T.opt_entityTypeId.isAvailable()) {
            if (commonEntityType.objectTypeId == -1) {
                throw new IllegalArgumentException("Input " + commonEntityType.toString() + " cannot be spawned using this packet");
            }
            T.opt_entityTypeId.setInteger(getRaw(), commonEntityType.objectTypeId);
        } else {
            if (commonEntityType.nmsEntityType == null) {
                throw new IllegalArgumentException("Input " + commonEntityType.toString() + " cannot be spawned using this packet");
            }
            T.opt_entityType.set(getRaw(), commonEntityType.nmsEntityType);
        }
        if (commonEntityType.objectExtraData != -1) {
            setExtraData(commonEntityType.objectExtraData);
        }
    }

    public com.bergerkiller.bukkit.common.entity.CommonEntityType getCommonEntityType() {
        if (T.opt_entityTypeId.isAvailable()) {
            int id = T.opt_entityTypeId.getInteger(getRaw());
            return com.bergerkiller.bukkit.common.entity.CommonEntityType.byObjectTypeId(id);
        } else {
            EntityTypesHandle nmsType = T.opt_entityType.get(getRaw());
            return com.bergerkiller.bukkit.common.entity.CommonEntityType.byNMSEntityType(nmsType);
        }
    }

    public void setEntityType(org.bukkit.entity.EntityType type) {
        setCommonEntityType(com.bergerkiller.bukkit.common.entity.CommonEntityType.byEntityType(type));
    }

    public org.bukkit.entity.EntityType getEntityType() {
        return getCommonEntityType().entityType;
    }

    @Deprecated
    public void setEntityTypeId(int typeId) {
        if (T.opt_entityTypeId.isAvailable()) {
            T.opt_entityTypeId.setInteger(getRaw(), typeId);
        } else {
            setCommonEntityType(com.bergerkiller.bukkit.common.entity.CommonEntityType.byObjectTypeId(typeId));
        }
    }

    @Deprecated
    public int getEntityTypeId() {
        if (T.opt_entityTypeId.isAvailable()) {
            return T.opt_entityTypeId.getInteger(getRaw());
        } else {
            return getCommonEntityType().objectTypeId;
        }
    }

    public void setFallingBlockData(com.bergerkiller.bukkit.common.wrappers.BlockData blockData) {
        setExtraData(blockData.getCombinedId());
    }

    public double getPosX() {
        return getProtocolPosition(T.posX_1_8_8, T.posX_1_10_2);
    }

    public double getPosY() {
        return getProtocolPosition(T.posY_1_8_8, T.posY_1_10_2);
    }

    public double getPosZ() {
        return getProtocolPosition(T.posZ_1_8_8, T.posZ_1_10_2);
    }

    public void setPosX(double posX) {
        setProtocolPosition(T.posX_1_8_8, T.posX_1_10_2, posX);
    }

    public void setPosY(double posY) {
        setProtocolPosition(T.posY_1_8_8, T.posY_1_10_2, posY);
    }

    public void setPosZ(double posZ) {
        setProtocolPosition(T.posZ_1_8_8, T.posZ_1_10_2, posZ);
    }
    </code>
}

// Note: since MC 1.20.2 this is identical to PacketPlayOutSpawnEntity
class PacketPlayOutNamedEntitySpawn extends Packet {
#if version >= 1.20.5
    #bootstrap com.bergerkiller.bukkit.common.internal.CommonBootstrap.initServer();
#endif

#if version >= 1.20.2
    private int entityId:id;
    private UUID entityUUID:uuid;
#elseif version >= 1.17
    private int entityId;
    private UUID entityUUID:playerId;
#else
    private int entityId:a;
    private UUID entityUUID:b;
#endif

#if version >= 1.17
    private optional int posX_1_8_8:###;
    private optional int posY_1_8_8:###;
    private optional int posZ_1_8_8:###;
    private optional double posX_1_10_2:x;
    private optional double posY_1_10_2:y;
    private optional double posZ_1_10_2:z;
#elseif version >= 1.9
    private optional int posX_1_8_8:###;
    private optional int posY_1_8_8:###;
    private optional int posZ_1_8_8:###;
    private optional double posX_1_10_2:c;
    private optional double posY_1_10_2:d;
    private optional double posZ_1_10_2:e;
#else
    private optional int posX_1_8_8:c;
    private optional int posY_1_8_8:d;
    private optional int posZ_1_8_8:e;
    private optional double posX_1_10_2:###;
    private optional double posY_1_10_2:###;
    private optional double posZ_1_10_2:###;
#endif

#if version >= 1.17
    private optional byte yaw_raw:yRot;
    private optional byte pitch_raw:xRot;
#else
    private optional byte yaw_raw:f;
    private optional byte pitch_raw:g;
#endif

#if version >= 1.9
    private optional (org.bukkit.Material) int heldItem:###;
#else
    private optional (org.bukkit.Material) int heldItem:h;
#endif

#if version >= 1.15
    private optional (com.bergerkiller.bukkit.common.wrappers.DataWatcher) DataWatcher opt_dataWatcher:###;
#elseif version >= 1.9
    private optional (com.bergerkiller.bukkit.common.wrappers.DataWatcher) DataWatcher opt_dataWatcher:h;
    private unknown List<DataWatcher.Item<?>> dataWatcherItems:i;
#else
    private optional (com.bergerkiller.bukkit.common.wrappers.DataWatcher) DataWatcher opt_dataWatcher:i;
    private unknown List<DataWatcher.WatchableObject> dataWatcherItems:j;
#endif

#if version >= 1.21.9
    #require PacketPlayOutNamedEntitySpawn private final Vec3D movement;
#endif

    public static (PacketPlayOutNamedEntitySpawnHandle) PacketPlayOutNamedEntitySpawn createNew() {
#if version >= 1.20.5
        #require net.minecraft.network.protocol.game.PacketPlayOutNamedEntitySpawn private PacketPlayOutNamedEntitySpawn createSpawnPacket:<init>(net.minecraft.network.RegistryFriendlyByteBuf serializer);
        PacketPlayOutNamedEntitySpawn packet = #createSpawnPacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
        #require PacketPlayOutNamedEntitySpawn private final EntityTypes<?> type;
        packet#type = EntityTypes.PLAYER;
  #if version >= 1.21.9
        packet#movement = Vec3D.ZERO;
  #endif
        return packet;
#elseif version >= 1.20.2
        PacketPlayOutNamedEntitySpawn packet = new PacketPlayOutNamedEntitySpawn(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
        #require PacketPlayOutNamedEntitySpawn private final EntityTypes<?> type;
        packet#type = EntityTypes.PLAYER;
        return packet;
#elseif version >= 1.17
        return new PacketPlayOutNamedEntitySpawn(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#else
        return new PacketPlayOutNamedEntitySpawn();
#endif
    }

    <code>
    public boolean hasDataWatcherSupport() {
        return T.opt_dataWatcher.isAvailable();
    }

    @Deprecated
    public com.bergerkiller.bukkit.common.wrappers.DataWatcher getDataWatcher() {
        if (T.opt_dataWatcher.isAvailable()) {
            return T.opt_dataWatcher.get(getRaw());
        } else {
            return null;
        }
    }

    @Deprecated
    public void setDataWatcher(com.bergerkiller.bukkit.common.wrappers.DataWatcher dataWatcher) {
        if (T.opt_dataWatcher.isAvailable()) {
            T.opt_dataWatcher.set(getRaw(), dataWatcher);
        }
    }

    @Override
    public com.bergerkiller.bukkit.common.protocol.PacketType getPacketType() {
        return com.bergerkiller.bukkit.common.protocol.PacketType.OUT_ENTITY_SPAWN_NAMED;
    }

    public double getPosX() {
        return getProtocolPosition(T.posX_1_8_8, T.posX_1_10_2);
    }

    public double getPosY() {
        return getProtocolPosition(T.posY_1_8_8, T.posY_1_10_2);
    }

    public double getPosZ() {
        return getProtocolPosition(T.posZ_1_8_8, T.posZ_1_10_2);
    }

    public void setPosX(double posX) {
        setProtocolPosition(T.posX_1_8_8, T.posX_1_10_2, posX);
    }

    public void setPosY(double posY) {
        setProtocolPosition(T.posY_1_8_8, T.posY_1_10_2, posY);
    }

    public void setPosZ(double posZ) {
        setProtocolPosition(T.posZ_1_8_8, T.posZ_1_10_2, posZ);
    }

    public float getYaw() {
        return getProtocolRotation(T.yaw_raw);
    }

    public float getPitch() {
        return getProtocolRotation(T.pitch_raw);
    }

    public void setYaw(float yaw) {
        setProtocolRotation(T.yaw_raw, yaw);
    }

    public void setPitch(float pitch) {
        setProtocolRotation(T.pitch_raw, pitch);
    }

    public void setHeldItem(Material type) {
        if (T.heldItem.isAvailable()) {
            T.heldItem.set(getRaw(), type);
        }
    }

    public org.bukkit.Material getHeldItem() {
        if (T.heldItem.isAvailable()) {
            return T.heldItem.get(getRaw());
        } else {
            return org.bukkit.Material.AIR;
        }
    }
    </code>
}

class PacketPlayOutSpawnEntityLiving extends Packet {
#if version >= 1.20.5
    #bootstrap com.bergerkiller.bukkit.common.internal.CommonBootstrap.initServer();
#endif

#if version >= 1.17
    private int entityId:id;
#else
    private int entityId:a;
#endif

#if version >= 1.19
    private optional UUID entityUUID:uuid;
    private optional byte pitch_raw:xRot;
    private optional byte yaw_raw:yRot;
    private optional byte headYaw_raw:yHeadRot;
#elseif version >= 1.17
    private optional UUID entityUUID:uuid;
    private optional byte yaw_raw:yRot;
    private optional byte pitch_raw:xRot;
    private optional byte headYaw_raw:yHeadRot;
#elseif version >= 1.9
    private optional UUID entityUUID:b;
    private optional byte yaw_raw:j;
    private optional byte pitch_raw:k;
    private optional byte headYaw_raw:l;
#else
    private optional UUID entityUUID:###;
    private optional byte yaw_raw:i;
    private optional byte pitch_raw:j;
    private optional byte headYaw_raw:k;
#endif

#if version >= 1.21.9
    #require PacketPlayOutSpawnEntityLiving private final Vec3D movement;
#elseif version >= 1.19
    #require PacketPlayOutSpawnEntityLiving private int motX_raw:xa;
    #require PacketPlayOutSpawnEntityLiving private int motY_raw:ya;
    #require PacketPlayOutSpawnEntityLiving private int motZ_raw:za;
#elseif version >= 1.17
    #require PacketPlayOutSpawnEntityLiving private int motX_raw:xd;
    #require PacketPlayOutSpawnEntityLiving private int motY_raw:yd;
    #require PacketPlayOutSpawnEntityLiving private int motZ_raw:zd;
#elseif version >= 1.9
    #require PacketPlayOutSpawnEntityLiving private int motX_raw:g;
    #require PacketPlayOutSpawnEntityLiving private int motY_raw:h;
    #require PacketPlayOutSpawnEntityLiving private int motZ_raw:i;
#else
    #require PacketPlayOutSpawnEntityLiving private int motX_raw:f;
    #require PacketPlayOutSpawnEntityLiving private int motY_raw:g;
    #require PacketPlayOutSpawnEntityLiving private int motZ_raw:h;
#endif

    // DataWatcher and items list was removed since MC 1.15
#if version >= 1.15
    private optional (com.bergerkiller.bukkit.common.wrappers.DataWatcher) DataWatcher opt_dataWatcher:###;
#elseif version >= 1.9
    private optional (com.bergerkiller.bukkit.common.wrappers.DataWatcher) DataWatcher opt_dataWatcher:m;
    private unknown List<DataWatcher.Item<?>> dataWatcherItems:n;
#else
    private optional (com.bergerkiller.bukkit.common.wrappers.DataWatcher) DataWatcher opt_dataWatcher:l;
    private unknown List<DataWatcher.WatchableObject> dataWatcherItems:m;
#endif

    public static (PacketPlayOutSpawnEntityLivingHandle) PacketPlayOutSpawnEntityLiving createNew() {
#if version >= 1.20.5
        #require net.minecraft.network.protocol.game.PacketPlayOutSpawnEntityLiving private PacketPlayOutSpawnEntityLiving createSpawnPacket:<init>(net.minecraft.network.RegistryFriendlyByteBuf serializer);
        PacketPlayOutSpawnEntityLiving packet = #createSpawnPacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
  #if version >= 1.21.9
        packet#movement = Vec3D.ZERO;
  #endif
        return packet;
#elseif version >= 1.17
        return new PacketPlayOutSpawnEntityLiving(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#else
        return new PacketPlayOutSpawnEntityLiving();
#endif
    }

    // Entity type property
#if version >= 1.19
    #require PacketPlayOutSpawnEntityLiving private final EntityTypes<?> entityType:type;

    public com.bergerkiller.bukkit.common.entity.CommonEntityType getCommonEntityType() {
        Object nmsEntityType = instance.getType();
        return com.bergerkiller.bukkit.common.entity.CommonEntityType.byNMSEntityTypeRaw(nmsEntityType);
    }

    public void setCommonEntityType(com.bergerkiller.bukkit.common.entity.CommonEntityType commonEntityType) {
        if (commonEntityType == null) {
            throw new IllegalArgumentException("Input CommonEntityType is null");
        }
        instance#entityType = (EntityTypes) commonEntityType.nmsEntityType.getRaw();
    }
#else
  #if version >= 1.17
    #require PacketPlayOutSpawnEntityLiving private int entityTypeId:type;
  #elseif version >= 1.9
    #require PacketPlayOutSpawnEntityLiving private int entityTypeId:c;
  #else
    #require PacketPlayOutSpawnEntityLiving private int entityTypeId:b;
  #endif

    public com.bergerkiller.bukkit.common.entity.CommonEntityType getCommonEntityType() {
        int typeId = instance#entityTypeId;
        return com.bergerkiller.bukkit.common.entity.CommonEntityType.byEntityTypeId(typeId);
    }

    public void setCommonEntityType(com.bergerkiller.bukkit.common.entity.CommonEntityType commonEntityType) {
        if (commonEntityType == null) {
            throw new IllegalArgumentException("Input CommonEntityType is null");
        }
        if (commonEntityType.entityTypeId == -1) {
            throw new IllegalArgumentException("Input CommonEntityType " + commonEntityType.toString() + " cannot be spawned using this packet");
        }
        instance#entityTypeId = commonEntityType.entityTypeId;
    }
#endif

    // Position property
#if version >= 1.17
    // Has getter accessor methods. Fields are double x/y/z
  #if version >= 1.18
    public double getPosX:getX();
    public double getPosY:getY();
    public double getPosZ:getZ();
  #else
    public double getPosX:e();
    public double getPosY:f();
    public double getPosZ:g();
  #endif

    // Used by setters down below
    #require PacketPlayOutSpawnEntityLiving private final double posX:x;
    #require PacketPlayOutSpawnEntityLiving private final double posY:y;
    #require PacketPlayOutSpawnEntityLiving private final double posZ:z;

#elseif version >= 1.9
    // Used by getters/setters down below
    #require PacketPlayOutSpawnEntityLiving private double posX:d;
    #require PacketPlayOutSpawnEntityLiving private double posY:e;
    #require PacketPlayOutSpawnEntityLiving private double posZ:f;

    public double getPosX() {
        return instance#posX;
    }
    public double getPosY() {
        return instance#posY;
    }
    public double getPosZ() {
        return instance#posZ;
    }
#else
    // Uses private int fields, requiring protocol encoding/decoding
    // Used by getters/setters down below
    #require PacketPlayOutSpawnEntityLiving private int posX:c;
    #require PacketPlayOutSpawnEntityLiving private int posY:d;
    #require PacketPlayOutSpawnEntityLiving private int posZ:e;

    public double getPosX() {
        int prot = instance#posX;
        return ProtocolMath.deserializePosition_1_8_8(prot);
    }
    public double getPosY() {
        int prot = instance#posY;
        return ProtocolMath.deserializePosition_1_8_8(prot);
    }
    public double getPosZ() {
        int prot = instance#posZ;
        return ProtocolMath.deserializePosition_1_8_8(prot);
    }
#endif
#if version >= 1.9
    public void setPosX(double x) {
        instance#posX = x;
    }
    public void setPosY(double y) {
        instance#posY = y;
    }
    public void setPosZ(double z) {
        instance#posZ = z;
    }
#else
    public void setPosX(double x) {
        int prot = ProtocolMath.serializePosition_1_8_8(x);
        instance#posX = prot;
    }
    public void setPosY(double y) {
        int prot = ProtocolMath.serializePosition_1_8_8(y);
        instance#posY = prot;
    }
    public void setPosZ(double z) {
        int prot = ProtocolMath.serializePosition_1_8_8(z);
        instance#posZ = prot;
    }
#endif

    // Velocity (Motion)
#if version >= 1.21.9
    public (org.bukkit.util.Vector) Vec3D getMotVector:getMovement();

    public void setMotVector((org.bukkit.util.Vector) Vec3D motVector) {
        instance#movement = motVector;
    }

    public double getMotX() { return instance.getMovement().x; }
    public double getMotY() { return instance.getMovement().y; }
    public double getMotZ() { return instance.getMovement().z; }

    public void setMotX(double motX) {
        Vec3D movement = instance.getMovement();
        if (movement == null) movement = Vec3D.ZERO; // Bugfix for past use of newHandleNull()
        Vec3D updated = new Vec3D(motX, movement.y, movement.z);
        instance#movement = updated;
    }

    public void setMotY(double motY) {
        Vec3D movement = instance.getMovement();
        if (movement == null) movement = Vec3D.ZERO; // Bugfix for past use of newHandleNull()
        Vec3D updated = new Vec3D(movement.x, motY, movement.z);
        instance#movement = updated;
    }

    public void setMotZ(double motZ) {
        Vec3D movement = instance.getMovement();
        if (movement == null) movement = Vec3D.ZERO; // Bugfix for past use of newHandleNull()
        Vec3D updated = new Vec3D(movement.x, movement.y, motZ);
        instance#movement = updated;
    }
#else
    public (org.bukkit.util.Vector) Vec3D getMotVector() {
        int motX_raw = instance#motX_raw;
        int motY_raw = instance#motY_raw;
        int motZ_raw = instance#motZ_raw;
        return new Vec3D( com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motX_raw),
                          com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motY_raw),
                          com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motZ_raw) );
    }

    public void setMotVector((org.bukkit.util.Vector) Vec3D motVector) {
        int motX_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motVector.x);
        int motY_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motVector.y);
        int motZ_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motVector.z);
        instance#motX_raw = motX_raw;
        instance#motY_raw = motY_raw;
        instance#motZ_raw = motZ_raw;
    }

    public double getMotX() {
        int motX_raw = instance#motX_raw;
        return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motX_raw);
    }

    public double getMotY() {
        int motY_raw = instance#motY_raw;
        return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motY_raw);
    }

    public double getMotZ() {
        int motZ_raw = instance#motZ_raw;
        return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motZ_raw);
    }

    public void setMotX(double motX) {
        int motX_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motX);
        instance#motX_raw = motX_raw;
    }

    public void setMotY(double motY) {
        int motY_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motY);
        instance#motY_raw = motY_raw;
    }

    public void setMotZ(double motZ) {
        int motZ_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motZ);
        instance#motZ_raw = motZ_raw;
    }
#endif

    <code>
    public boolean hasDataWatcherSupport() {
        return T.opt_dataWatcher.isAvailable();
    }

    @Deprecated
    public com.bergerkiller.bukkit.common.wrappers.DataWatcher getDataWatcher() {
        if (T.opt_dataWatcher.isAvailable()) {
            return T.opt_dataWatcher.get(getRaw());
        } else {
            return null;
        }
    }

    @Deprecated
    public void setDataWatcher(com.bergerkiller.bukkit.common.wrappers.DataWatcher dataWatcher) {
        if (T.opt_dataWatcher.isAvailable()) {
            T.opt_dataWatcher.set(getRaw(), dataWatcher);
        }
    }

    @Override
    public com.bergerkiller.bukkit.common.protocol.PacketType getPacketType() {
        return com.bergerkiller.bukkit.common.protocol.PacketType.OUT_ENTITY_SPAWN_LIVING;
    }

    public void setEntityUUID(UUID uuid) {
        if (T.entityUUID.isAvailable()) {
            T.entityUUID.set(getRaw(), uuid);
        }
    }

    public static boolean isCommonEntityTypeSupported(com.bergerkiller.bukkit.common.entity.CommonEntityType type) {
        return type != null && type.entityTypeId != -1;
    }

    public static boolean isEntityTypeSupported(org.bukkit.entity.EntityType type) {
        return type != null && isCommonEntityTypeSupported(com.bergerkiller.bukkit.common.entity.CommonEntityType.byEntityType(type));
    }

    public void setEntityType(org.bukkit.entity.EntityType type) {
        if (type == null) {
            throw new IllegalArgumentException("Input EntityType is null");
        }
        setCommonEntityType(com.bergerkiller.bukkit.common.entity.CommonEntityType.byEntityType(type));
    }

    public org.bukkit.entity.EntityType getEntityType() {
        return getCommonEntityType().entityType;
    }

    public float getYaw() {
        return getProtocolRotation(T.yaw_raw);
    }

    public float getPitch() {
        return getProtocolRotation(T.pitch_raw);
    }

    public float getHeadYaw() {
        return getProtocolRotation(T.headYaw_raw);
    }

    public void setYaw(float yaw) {
        setProtocolRotation(T.yaw_raw, yaw);
    }

    public void setPitch(float pitch) {
        setProtocolRotation(T.pitch_raw, pitch);
    }

    public void setHeadYaw(float headYaw) {
        setProtocolRotation(T.headYaw_raw, headYaw);
    }
    </code>
}

// No longer used since 1.21.5, where it uses a normal PacketPlayOutSpawnEntity
// The experience value (size of orb) is stored in the DataWatcher metadata instead
// This logic is incompatible so this class is not kept around for 1.21.5+
optional class PacketPlayOutSpawnEntityExperienceOrb extends Packet {
#if version >= 1.17
    private int entityId:id;
#else
    private int entityId:a;
#endif

#if version >= 1.17
    private optional int posX_1_8_8:###;
    private optional int posY_1_8_8:###;
    private optional int posZ_1_8_8:###;
    private optional double posX_1_10_2:x;
    private optional double posY_1_10_2:y;
    private optional double posZ_1_10_2:z;
#elseif version >= 1.9
    private optional int posX_1_8_8:###;
    private optional int posY_1_8_8:###;
    private optional int posZ_1_8_8:###;
    private optional double posX_1_10_2:b;
    private optional double posY_1_10_2:c;
    private optional double posZ_1_10_2:d;
#else
    private optional int posX_1_8_8:b;
    private optional int posY_1_8_8:c;
    private optional int posZ_1_8_8:d;
    private optional double posX_1_10_2:###;
    private optional double posY_1_10_2:###;
    private optional double posZ_1_10_2:###;
#endif

#if version >= 1.17
    private int experience:value;
#else
    private int experience:e;
#endif

    <code>
    public double getPosX() {
        return getProtocolPosition(T.posX_1_8_8, T.posX_1_10_2);
    }

    public double getPosY() {
        return getProtocolPosition(T.posY_1_8_8, T.posY_1_10_2);
    }

    public double getPosZ() {
        return getProtocolPosition(T.posZ_1_8_8, T.posZ_1_10_2);
    }

    public void setPosX(double posX) {
        setProtocolPosition(T.posX_1_8_8, T.posX_1_10_2, posX);
    }

    public void setPosY(double posY) {
        setProtocolPosition(T.posY_1_8_8, T.posY_1_10_2, posY);
    }

    public void setPosZ(double posZ) {
        setProtocolPosition(T.posZ_1_8_8, T.posZ_1_10_2, posZ);
    }
    </code>
}

class PacketPlayOutSpawnEntityPainting extends Packet {
#if version >= 1.17
    private int entityId:id;
#else
    private int entityId:a;
#endif

#if version >= 1.19
    // Art field doesn't exist. Is entity metadata now
    public static boolean hasArtField() {
        return false;
    }
    public (org.bukkit.Art) Object getArt() {
        return null;
    }
    public void setArt((org.bukkit.Art) Object art) {
    }

    // Position/Facing integrated in PacketPlayOutSpawnEntity itself as position/data
    public (IntVector3) BlockPosition getPosition() {
  #if version >= 1.19.4
        return BlockPosition.containing(instance.getX(), instance.getY(), instance.getZ());
  #else
        return new BlockPosition(instance.getX(), instance.getY(), instance.getZ());
  #endif
    }
    public void setPosition((IntVector3) BlockPosition position) {
        #require PacketPlayOutSpawnEntityPainting private final double posX:x;
        #require PacketPlayOutSpawnEntityPainting private final double posY:y;
        #require PacketPlayOutSpawnEntityPainting private final double posZ:z;
        instance#posX = (double)position.getX();
        instance#posY = (double)position.getY();
        instance#posZ = (double)position.getZ();
    }
    public (BlockFace) EnumDirection getFacing() {
        return EnumDirection.from3DDataValue(instance.getData());
    }
    public void setFacing((BlockFace) EnumDirection facing) {
        #require PacketPlayOutSpawnEntityPainting private final int data;
        instance#data = facing.get3DDataValue();
    }
#else
    // Art field is an int index into a registry
    public static boolean hasArtField() {
        return true;
    }
  #select version >=
  #case 1.17:   #require PacketPlayOutSpawnEntityPainting private int artId:motive;
  #case 1.13:   #require PacketPlayOutSpawnEntityPainting private int artId:e;
  #case 1.9:    #require PacketPlayOutSpawnEntityPainting private String artName:e;
  #case else:   #require PacketPlayOutSpawnEntityPainting private String artName:d;
  #endselect
  #if version >= 1.13
    public (org.bukkit.Art) int getArt() {
        return instance#artId;
    }
    public void setArt((org.bukkit.Art) int art) {
        instance#artId = art;
    }
  #else
    public (org.bukkit.Art) String getArt() {
        return instance#artName;
    }
    public void setArt((org.bukkit.Art) String art) {
        instance#artName = art;
    }
  #endif

    // Position/Facing were fields of their own
  #if version >= 1.17
    #require PacketPlayOutSpawnEntityPainting private BlockPosition position:pos;
    #require PacketPlayOutSpawnEntityPainting private EnumDirection facing:direction;
  #elseif version >= 1.9
    #require PacketPlayOutSpawnEntityPainting private BlockPosition position:c;
    #require PacketPlayOutSpawnEntityPainting private EnumDirection facing:d;
  #else
    #require PacketPlayOutSpawnEntityPainting private BlockPosition position:b;
    #require PacketPlayOutSpawnEntityPainting private EnumDirection facing:c;
  #endif

    public (IntVector3) BlockPosition getPosition() {
        return instance#position;
    }
    public void setPosition((IntVector3) BlockPosition position) {
        instance#position = position;
    }
    public (BlockFace) EnumDirection getFacing() {
        return instance#facing;
    }
    public void setFacing((BlockFace) EnumDirection facing) {
        instance#facing = facing;
    }
#endif

    public void setEntityUUID(UUID uuid) {
#if version >= 1.17
        #require PacketPlayOutSpawnEntityPainting private UUID entityUUID:uuid;
        instance#entityUUID = uuid;
#elseif version >= 1.9
        #require PacketPlayOutSpawnEntityPainting private UUID entityUUID:b;
        instance#entityUUID = uuid;
#endif
    }
}

// This class is used from Minecraft 1.8 to 1.15.2
// Since Minecraft 1.16 the PacketPlayOutSpawnEntity packet is used instead,
// and this packet was removed.
optional class PacketPlayOutSpawnEntityWeather extends Packet {
    private int entityId:a;

#if version >= 1.9
    private optional int posX_1_8_8:###;
    private optional int posY_1_8_8:###;
    private optional int posZ_1_8_8:###;
    private optional double posX_1_10_2:b;
    private optional double posY_1_10_2:c;
    private optional double posZ_1_10_2:d;
#else
    private optional int posX_1_8_8:b;
    private optional int posY_1_8_8:c;
    private optional int posZ_1_8_8:d;
    private optional double posX_1_10_2:###;
    private optional double posY_1_10_2:###;
    private optional double posZ_1_10_2:###;
#endif

    private int type:e;

    <code>
    public double getPosX() {
        return getProtocolPosition(T.posX_1_8_8, T.posX_1_10_2);
    }

    public double getPosY() {
        return getProtocolPosition(T.posY_1_8_8, T.posY_1_10_2);
    }

    public double getPosZ() {
        return getProtocolPosition(T.posZ_1_8_8, T.posZ_1_10_2);
    }

    public void setPosX(double posX) {
        setProtocolPosition(T.posX_1_8_8, T.posX_1_10_2, posX);
    }

    public void setPosY(double posY) {
        setProtocolPosition(T.posY_1_8_8, T.posY_1_10_2, posY);
    }

    public void setPosZ(double posZ) {
        setProtocolPosition(T.posZ_1_8_8, T.posZ_1_10_2, posZ);
    }
    </code>
}