package net.minecraft.network.protocol.game;

import net.minecraft.network.protocol.Packet;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.PositionMoveRotation;
import net.minecraft.world.phys.Vec3D;
import net.minecraft.world.entity.vehicle.NewMinecartBehavior.LerpStep;

import com.bergerkiller.bukkit.common.internal.logic.ProtocolMath;

import com.bergerkiller.generated.net.minecraft.network.protocol.game.PacketPlayOutEntityTeleportHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.PacketPlayOutEntityHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.PacketPlayOutEntityHandle.PacketPlayOutRelEntityMoveHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.PacketPlayOutEntityHandle.PacketPlayOutRelEntityMoveLookHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.PacketPlayOutEntityHandle.PacketPlayOutEntityLookHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.PacketPlayOutEntityHeadRotationHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.PacketPlayOutEntityVelocityHandle
import com.bergerkiller.generated.net.minecraft.network.protocol.game.PacketPlayOutVehicleMoveHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.PacketPlayInVehicleMoveHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.ClientboundMoveMinecartPacketHandle;

import com.bergerkiller.generated.net.minecraft.world.entity.vehicle.NewMinecartBehaviorHandle.LerpStepHandle;

import com.bergerkiller.bukkit.common.wrappers.RelativeFlags;

// Only >= MC 1.9
optional class PacketPlayInVehicleMove extends Packet {
#if version >= 1.21.4
    public double getPosX() { return instance.position().x; }
    public double getPosY() { return instance.position().y; }
    public double getPosZ() { return instance.position().z; }
    public float getYaw:yRot();
    public float getPitch:xRot();
    public boolean isOnGround:onGround();

    public static (PacketPlayInVehicleMoveHandle) PacketPlayInVehicleMove createNew(double posX, double posY, double posZ, float yaw, float pitch, boolean onGround) {
        return new PacketPlayInVehicleMove(new Vec3D(posX, posY, posZ), yaw, pitch, onGround);
    }
#else
  #if version >= 1.17
    #require PacketPlayInVehicleMove private double posX:x;
    #require PacketPlayInVehicleMove private double posY:y;
    #require PacketPlayInVehicleMove private double posZ:z;
    #require PacketPlayInVehicleMove private float yaw:yRot;
    #require PacketPlayInVehicleMove private float pitch:xRot;
  #elseif version >= 1.9
    #require PacketPlayInVehicleMove private double posX:a;
    #require PacketPlayInVehicleMove private double posY:b;
    #require PacketPlayInVehicleMove private double posZ:c;
    #require PacketPlayInVehicleMove private float yaw:d;
    #require PacketPlayInVehicleMove private float pitch:e;
  #endif

    public double getPosX() { return instance#posX; }
    public double getPosY() { return instance#posY; }
    public double getPosZ() { return instance#posZ; }
    public float getYaw() { return instance#yaw; }
    public float getPitch() { return instance#pitch; }
    public boolean isOnGround() { return false; /* not supported */ }

    public static (PacketPlayInVehicleMoveHandle) PacketPlayInVehicleMove createNew(double posX, double posY, double posZ, float yaw, float pitch, boolean onGround) {
  #if version >= 1.17
        #require PacketPlayInVehicleMove private PacketPlayInVehicleMove createVehicleMovePacket:<init>(net.minecraft.network.PacketDataSerializer packetdataserializer);
        PacketPlayInVehicleMove packet = #createVehicleMovePacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
  #else
        PacketPlayInVehicleMove packet = new PacketPlayInVehicleMove();
  #endif
        packet#posX = posX;
        packet#posY = posY;
        packet#posZ = posZ;
        packet#yaw = yaw;
        packet#pitch = pitch;
        //onGround is ignored (doesn't exist)
        return packet;
    }
#endif
}

// Only >= MC 1.9
optional class PacketPlayOutVehicleMove extends Packet {
#if version >= 1.21.4
    public double getPosX() { return instance.position().x; }
    public double getPosY() { return instance.position().y; }
    public double getPosZ() { return instance.position().z; }
    public float getYaw:yRot();
    public float getPitch:xRot();

    public static (PacketPlayOutVehicleMoveHandle) PacketPlayOutVehicleMove createNew(double posX, double posY, double posZ, float yaw, float pitch) {
        return new PacketPlayOutVehicleMove(new Vec3D(posX, posY, posZ), yaw, pitch);
    }
#else
  #if version >= 1.17
    #require PacketPlayOutVehicleMove private double posX:x;
    #require PacketPlayOutVehicleMove private double posY:y;
    #require PacketPlayOutVehicleMove private double posZ:z;
    #require PacketPlayOutVehicleMove private float yaw:yRot;
    #require PacketPlayOutVehicleMove private float pitch:xRot;
  #elseif version >= 1.9
    #require PacketPlayOutVehicleMove private double posX:a;
    #require PacketPlayOutVehicleMove private double posY:b;
    #require PacketPlayOutVehicleMove private double posZ:c;
    #require PacketPlayOutVehicleMove private float yaw:d;
    #require PacketPlayOutVehicleMove private float pitch:e;
  #endif

    public double getPosX() { return instance#posX; }
    public double getPosY() { return instance#posY; }
    public double getPosZ() { return instance#posZ; }
    public float getYaw() { return instance#yaw; }
    public float getPitch() { return instance#pitch; }

    public static (PacketPlayOutVehicleMoveHandle) PacketPlayOutVehicleMove createNew(double posX, double posY, double posZ, float yaw, float pitch) {
  #if version >= 1.17
        #require PacketPlayOutVehicleMove private PacketPlayOutVehicleMove createVehicleMovePacket:<init>(net.minecraft.network.PacketDataSerializer packetdataserializer);
        PacketPlayOutVehicleMove packet = #createVehicleMovePacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
  #else
        PacketPlayOutVehicleMove packet = new PacketPlayOutVehicleMove();
  #endif
        packet#posX = posX;
        packet#posY = posY;
        packet#posZ = posZ;
        packet#yaw = yaw;
        packet#pitch = pitch;
        return packet;
    }
#endif
}

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

    // Reflection needed on some versions of Minecraft, and also for creating a new packet
#if version >= 1.21.2
    // Record class, no fields need to be set using reflection
#elseif version >= 1.17
    #require PacketPlayOutEntityTeleport private final int entityId:id;
    #require PacketPlayOutEntityTeleport private final double posX:x;
    #require PacketPlayOutEntityTeleport private final double posY:y;
    #require PacketPlayOutEntityTeleport private final double posZ:z;
    #require PacketPlayOutEntityTeleport private final byte encodedYaw:yRot;
    #require PacketPlayOutEntityTeleport private final byte encodedPitch:xRot;
    #require PacketPlayOutEntityTeleport private final boolean onGround;
#else
    #require PacketPlayOutEntityTeleport private int entityId:a;
  #if version >= 1.9
    #require PacketPlayOutEntityTeleport private double posX:b;
    #require PacketPlayOutEntityTeleport private double posY:c;
    #require PacketPlayOutEntityTeleport private double posZ:d;
  #else
    #require PacketPlayOutEntityTeleport private int encodedPosX:b;
    #require PacketPlayOutEntityTeleport private int encodedPosY:c;
    #require PacketPlayOutEntityTeleport private int encodedPosZ:d;
  #endif
    #require PacketPlayOutEntityTeleport private byte encodedYaw:e;
    #require PacketPlayOutEntityTeleport private byte encodedPitch:f;
    #require PacketPlayOutEntityTeleport private boolean onGround:g;
#endif

#if version >= 1.21.2
    public int getEntityId:id();
    public double getPosX() { return instance.values().position().x; }
    public double getPosY() { return instance.values().position().y; }
    public double getPosZ() { return instance.values().position().z; }
    public float getYaw() { return instance.values().yRot(); }
    public float getPitch() { return instance.values().xRot(); }
    public boolean isOnGround:onGround();

    // Not really used anymore, but here for completion sake
    public int getEncodedPosX() {
        return ProtocolMath.serializePosition_1_8_8(instance.values().position().x);
    }
    public int getEncodedPosY() {
        return ProtocolMath.serializePosition_1_8_8(instance.values().position().y);
    }
    public int getEncodedPosZ() {
        return ProtocolMath.serializePosition_1_8_8(instance.values().position().z);
    }
    public int getEncodedYaw() {
        return ProtocolMath.serializeRotation(instance.values().yRot());
    }
    public int getEncodedPitch() {
        return ProtocolMath.serializeRotation(instance.values().xRot());
    }
#elseif version >= 1.18
    public int getEntityId:getId();
    public double getPosX:getX();
    public double getPosY:getY();
    public double getPosZ:getZ();
    public float getYaw() {
        return ProtocolMath.deserializeRotation(instance.getyRot());
    }
    public float getPitch() {
        return ProtocolMath.deserializeRotation(instance.getxRot());
    }
    public boolean isOnGround();

    // Not really used anymore, but here for completion sake
    public int getEncodedPosX() {
        return ProtocolMath.serializePosition_1_8_8(instance.getX());
    }
    public int getEncodedPosY() {
        return ProtocolMath.serializePosition_1_8_8(instance.getY());
    }
    public int getEncodedPosZ() {
        return ProtocolMath.serializePosition_1_8_8(instance.getZ());
    }

    public int getEncodedYaw() { return (int) instance.getyRot() & 0xFF; }
    public int getEncodedPitch() { return (int) instance.getxRot() & 0xFF; }
#elseif version >= 1.17
    public int getEntityId:b();
    public double getPosX:c();
    public double getPosY:d();
    public double getPosZ:e();
    public float getYaw() {
        return ProtocolMath.deserializeRotation(instance.f());
    }
    public float getPitch() {
        return ProtocolMath.deserializeRotation(instance.g());
    }
    public boolean isOnGround:h();

    // Not really used anymore, but here for completion sake
    public int getEncodedPosX() {
        return ProtocolMath.serializePosition_1_8_8(instance.c());
    }
    public int getEncodedPosY() {
        return ProtocolMath.serializePosition_1_8_8(instance.d());
    }
    public int getEncodedPosZ() {
        return ProtocolMath.serializePosition_1_8_8(instance.e());
    }

    public int getEncodedYaw() { return (int) instance.f() & 0xFF; }
    public int getEncodedPitch() { return (int) instance.g() & 0xFF; }
#else
    // Relies on reflection to access the private fields
    public int getEntityId() { return instance#entityId; }

  #if version >= 1.9
    public double getPosX() { return instance#posX; }
    public double getPosY() { return instance#posY; }
    public double getPosZ() { return instance#posZ; }
  #else
    public double getPosX() { return ProtocolMath.deserializePosition_1_8_8( instance#encodedPosX ); }
    public double getPosY() { return ProtocolMath.deserializePosition_1_8_8( instance#encodedPosY ); }
    public double getPosZ() { return ProtocolMath.deserializePosition_1_8_8( instance#encodedPosZ ); }
  #endif

    public float getYaw() {
        return ProtocolMath.deserializeRotation( instance#encodedYaw );
    }
    public float getPitch() {
        return ProtocolMath.deserializeRotation( instance#encodedPitch );
    }

    public boolean isOnGround() { return instance#onGround; }

  #if version >= 1.9
    // Not really used anymore, but here for completion sake
    public int getEncodedPosX() {
        return ProtocolMath.serializePosition_1_8_8( instance#posX );
    }
    public int getEncodedPosY() {
        return ProtocolMath.serializePosition_1_8_8( instance#posY );
    }
    public int getEncodedPosZ() {
        return ProtocolMath.serializePosition_1_8_8( instance#posZ );
    }
  #else
    public int getEncodedPosX() { return instance#encodedPosX; }
    public int getEncodedPosY() { return instance#encodedPosY; }
    public int getEncodedPosZ() { return instance#encodedPosZ; }
  #endif

    public int getEncodedYaw() { return (int) instance#encodedYaw & 0xFF; }
    public int getEncodedPitch() { return (int) instance#encodedPitch & 0xFF; }
#endif

    public static (PacketPlayOutEntityTeleportHandle) PacketPlayOutEntityTeleport createNewForEntity((org.bukkit.entity.Entity) Entity entity) {
#if version >= 1.21.2
        PositionMoveRotation values = PositionMoveRotation.of(entity);
        return new PacketPlayOutEntityTeleport(entity.getId(), values, entity.onGround());
#else
        return new PacketPlayOutEntityTeleport(entity);
#endif
    }

    public static (PacketPlayOutEntityTeleportHandle) PacketPlayOutEntityTeleport createNew(int entityId, double posX, double posY, double posZ, float yaw, float pitch, boolean onGround) {
#if version >= 1.21.2
        PositionMoveRotation values = new PositionMoveRotation(new Vec3D(posX, posY, posZ), new Vec3D(0.0, 0.0, 0.0), yaw, pitch);
        return new PacketPlayOutEntityTeleport(entityId, values, onGround);

#elseif version >= 1.9
        // Create a new packet and use reflection to set the fields
        // Unfortunately, no nice constructor :(
  #if version >= 1.20.5
        #require net.minecraft.network.protocol.game.PacketPlayOutEntityTeleport private PacketPlayOutEntityTeleport createTeleportPacket:<init>(net.minecraft.network.PacketDataSerializer serializer);
        PacketPlayOutEntityTeleport packet = #createTeleportPacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
  #elseif version >= 1.17
        PacketPlayOutEntityTeleport packet = new PacketPlayOutEntityTeleport(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
  #else
        PacketPlayOutEntityTeleport packet = new PacketPlayOutEntityTeleport();
  #endif

        byte encodedYaw = (byte) ProtocolMath.serializeRotation(yaw);
        byte encodedPitch = (byte) ProtocolMath.serializeRotation(pitch);

        packet#entityId = entityId;
        packet#posX = posX;
        packet#posY = posY;
        packet#posZ = posZ;
        packet#encodedYaw = encodedYaw;
        packet#encodedPitch = encodedPitch;
        packet#onGround = onGround;
        return packet;
#else
        // Constructor is available
        int encodedPosX = ProtocolMath.serializePosition_1_8_8(posX);
        int encodedPosY = ProtocolMath.serializePosition_1_8_8(posY);
        int encodedPosZ = ProtocolMath.serializePosition_1_8_8(posZ);
        byte encodedYaw = (byte) ProtocolMath.serializeRotation(yaw);
        byte encodedPitch = (byte) ProtocolMath.serializeRotation(pitch);
        return new PacketPlayOutEntityTeleport(entityId, encodedPosX, encodedPosY, encodedPosZ, encodedYaw, encodedPitch, onGround);
#endif
    }

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


    public static Builder builder() {
        return new Builder();
    }

    public static class Builder {
        private int entityId;
        private double posX, posY, posZ;
        private float yaw, pitch;
        private boolean onGround;

        public Builder entityId(int entityId) { this.entityId = entityId; return this; }
        public Builder posX(double posX) { this.posX = posX; return this; }
        public Builder posY(double posY) { this.posY = posY; return this; }
        public Builder posZ(double posZ) { this.posZ = posZ; return this; }
        public Builder position(org.bukkit.util.Vector position) {
            return position(position.getX(), position.getY(), position.getZ());
        }
        public Builder position(double x, double y, double z) {
            this.posX = x;
            this.posY = y;
            this.posZ = z;
            return this;
        }
        public Builder yaw(float yaw) { this.yaw = yaw; return this; }
        public Builder pitch(float pitch) { this.pitch = pitch; return this; }
        public Builder rotation(float yaw, float pitch) {
            this.yaw = yaw;
            this.pitch = pitch;
            return this;
        }
        public Builder onGround(boolean onGround) { this.onGround = onGround; return this; }

        public PacketPlayOutEntityTeleportHandle create() {
            return PacketPlayOutEntityTeleportHandle.createNew(entityId, posX, posY, posZ, yaw, pitch, onGround);
        }
    }
    </code>
}

// Only >= MC 1.9
optional class PacketPlayInBoatMove {
#if version >= 1.17
    private boolean leftPaddle:left;
    private boolean rightPaddle:right;
#else
    private boolean leftPaddle:a;
    private boolean rightPaddle:b;
#endif
}

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

    public static (PacketPlayOutEntityVelocityHandle) PacketPlayOutEntityVelocity createNew(int entityId, double motX, double motY, double motZ) {
#if version >= 1.14
        return new PacketPlayOutEntityVelocity(entityId, new Vec3D(motX, motY, motZ));
#else
        return new PacketPlayOutEntityVelocity(entityId, motX, motY, motZ);
#endif
    }

    public (PacketPlayOutEntityVelocityHandle) PacketPlayOutEntityVelocity((org.bukkit.entity.Entity) Entity entity);

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

    public (org.bukkit.util.Vector) Vec3D getMotVector:getMovement();

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

    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 x) {
        Vec3D movement = instance.getMovement();
        Vec3D updated = new Vec3D(x, movement.y, movement.z);
        instance#movement = updated;
    }
    public void setMotY(double y) {
        Vec3D movement = instance.getMovement();
        Vec3D updated = new Vec3D(movement.x, y, movement.z);
        instance#movement = updated;
    }
    public void setMotZ(double z) {
        Vec3D movement = instance.getMovement();
        Vec3D updated = new Vec3D(movement.x, movement.y, z);
        instance#movement = updated;
    }
#else
  #if version >= 1.17
    #require PacketPlayOutEntityVelocity private int motX_raw:xa;
    #require PacketPlayOutEntityVelocity private int motY_raw:ya;
    #require PacketPlayOutEntityVelocity private int motZ_raw:za;
  #else
    #require PacketPlayOutEntityVelocity private int motX_raw:b;
    #require PacketPlayOutEntityVelocity private int motY_raw:c;
    #require PacketPlayOutEntityVelocity private int motZ_raw:d;
  #endif

    public (org.bukkit.util.Vector) Vec3D getMotVector:getMovement() {
        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 movement) {
        int motX_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(movement.x);
        int motY_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(movement.y);
        int motZ_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(movement.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 x) {
        int motX_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(x);
        instance#motX_raw = motX_raw;
    }

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

    public void setMotZ(double z) {
        int motZ_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(z);
        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_VELOCITY;
    }
    </code>
}

class PacketPlayOutEntity extends Packet {
#if version >= 1.17
    protected int entityId;
    protected boolean onGround;
#else
    protected int entityId:a;
    protected boolean onGround:g;
#endif

#if version >= 1.14
  #if version >= 1.17
    #require net.minecraft.network.protocol.game.PacketPlayOutEntity protected short dx:xa;
    #require net.minecraft.network.protocol.game.PacketPlayOutEntity protected short dy:ya;
    #require net.minecraft.network.protocol.game.PacketPlayOutEntity protected short dz:za;
  #else
    #require net.minecraft.network.protocol.game.PacketPlayOutEntity protected short dx:b;
    #require net.minecraft.network.protocol.game.PacketPlayOutEntity protected short dy:c;
    #require net.minecraft.network.protocol.game.PacketPlayOutEntity protected short dz:d;
  #endif

    public double getDeltaX() {
        return ProtocolMath.deserializePosition_1_10_2(instance#dx);
    }

    public double getDeltaY() {
        return ProtocolMath.deserializePosition_1_10_2(instance#dy);
    }

    public double getDeltaZ() {
        return ProtocolMath.deserializePosition_1_10_2(instance#dz);
    }

    public void setDeltaX(double dx) {
        instance#dx = (short) ProtocolMath.serializePosition_1_10_2(dx);
    }

    public void setDeltaY(double dy) {
        instance#dy = (short) ProtocolMath.serializePosition_1_10_2(dy);
    }

    public void setDeltaZ(double dz) {
        instance#dz = (short) ProtocolMath.serializePosition_1_10_2(dz);
    }
#elseif version >= 1.9
    #require net.minecraft.network.protocol.game.PacketPlayOutEntity protected int dx:b;
    #require net.minecraft.network.protocol.game.PacketPlayOutEntity protected int dy:c;
    #require net.minecraft.network.protocol.game.PacketPlayOutEntity protected int dz:d;

    public double getDeltaX() {
        return ProtocolMath.deserializePosition_1_10_2(instance#dx);
    }

    public double getDeltaY() {
        return ProtocolMath.deserializePosition_1_10_2(instance#dy);
    }

    public double getDeltaZ() {
        return ProtocolMath.deserializePosition_1_10_2(instance#dz);
    }

    public void setDeltaX(double dx) {
        instance#dx = ProtocolMath.serializePosition_1_10_2(dx);
    }

    public void setDeltaY(double dy) {
        instance#dy = ProtocolMath.serializePosition_1_10_2(dy);
    }

    public void setDeltaZ(double dz) {
        instance#dz = ProtocolMath.serializePosition_1_10_2(dz);
    }
#else
    #require net.minecraft.network.protocol.game.PacketPlayOutEntity protected byte dx:b;
    #require net.minecraft.network.protocol.game.PacketPlayOutEntity protected byte dy:c;
    #require net.minecraft.network.protocol.game.PacketPlayOutEntity protected byte dz:d;

    public double getDeltaX() {
        return ProtocolMath.deserializePosition_1_8_8(instance#dx);
    }

    public double getDeltaY() {
        return ProtocolMath.deserializePosition_1_8_8(instance#dy);
    }

    public double getDeltaZ() {
        return ProtocolMath.deserializePosition_1_8_8(instance#dz);
    }

    public void setDeltaX(double dx) {
        instance#dx = (byte) ProtocolMath.serializePosition_1_8_8(dx);
    }

    public void setDeltaY(double dy) {
        instance#dy = (byte) ProtocolMath.serializePosition_1_8_8(dy);
    }

    public void setDeltaZ(double dz) {
        instance#dz = (byte) ProtocolMath.serializePosition_1_8_8(dz);
    }
#endif

#if version >= 1.17
    #require net.minecraft.network.protocol.game.PacketPlayOutEntity protected byte yaw:yRot;
    #require net.minecraft.network.protocol.game.PacketPlayOutEntity protected byte pitch:xRot;
#else
    #require net.minecraft.network.protocol.game.PacketPlayOutEntity protected byte yaw:e;
    #require net.minecraft.network.protocol.game.PacketPlayOutEntity protected byte pitch:f;
#endif

    public float getYaw() {
        return ProtocolMath.deserializeRotation(instance#yaw);
    }

    public float getPitch() {
        return ProtocolMath.deserializeRotation(instance#pitch);
    }

    public void setYaw(float yaw) {
        instance#yaw = (byte) ProtocolMath.serializeRotation(yaw);
    }

    public void setPitch(float pitch) {
        instance#pitch = (byte) ProtocolMath.serializeRotation(pitch);
    }

    <code>
    /**
     * Deprecated: is not actually a delta, use getYaw() instead
     */
    @Deprecated
    public float getDeltaYaw() {
        return getYaw();
    }

    /**
     * Deprecated: is not actually a delta, use getPitch() instead
     */
    @Deprecated
    public float getDeltaPitch() {
        return getPitch();
    }

    /**
     * Deprecated: is not actually a delta, use setYaw(yaw) instead
     */
    @Deprecated
    public void setDeltaYaw(float deltaYaw) {
        setYaw(deltaYaw);
    }

    /**
     * Deprecated: is not actually a delta, use setPitch(pitch) instead
     */
    @Deprecated
    public void setDeltaPitch(float deltaPitch) {
        setPitch(deltaPitch);
    }
    </code>

    class PacketPlayOutEntity.PacketPlayOutEntityLook extends PacketPlayOutEntity {
        public static (PacketPlayOutEntityHandle.PacketPlayOutEntityLookHandle) PacketPlayOutEntity.PacketPlayOutEntityLook createNew() {
#if version >= 1.20.5
            return new PacketPlayOutEntity$PacketPlayOutEntityLook(0, (byte) 0, (byte) 0, false);
#elseif version >= 1.18
            return PacketPlayOutEntity$PacketPlayOutEntityLook.read(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#elseif version >= 1.17
            return PacketPlayOutEntity$PacketPlayOutEntityLook.b(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#else
            return new PacketPlayOutEntity$PacketPlayOutEntityLook();
#endif
        }

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

        public static PacketPlayOutEntityHandle.PacketPlayOutEntityLookHandle createNew(int entityId, float yaw, float pitch, boolean onGround) {
            PacketPlayOutEntityHandle.PacketPlayOutEntityLookHandle handle = createNew();
            handle.setEntityId(entityId);
            handle.setYaw(yaw);
            handle.setPitch(pitch);
            handle.setOnGround(onGround);
            return handle;
        }
        </code>
    }

    class PacketPlayOutEntity.PacketPlayOutRelEntityMove extends PacketPlayOutEntity {
        public static (PacketPlayOutEntityHandle.PacketPlayOutRelEntityMoveHandle) PacketPlayOutEntity.PacketPlayOutRelEntityMove createNew() {
#if version >= 1.20.5
            return new PacketPlayOutEntity$PacketPlayOutRelEntityMove(0, (short) 0, (short) 0, (short) 0, false);
#elseif version >= 1.18
            return PacketPlayOutEntity$PacketPlayOutRelEntityMove.read(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#elseif version >= 1.17
            return PacketPlayOutEntity$PacketPlayOutRelEntityMove.b(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#else
            return new PacketPlayOutEntity$PacketPlayOutRelEntityMove();
#endif
        }

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

        public static PacketPlayOutEntityHandle.PacketPlayOutRelEntityMoveHandle createNew(int entityId, double dx, double dy, double dz, boolean onGround) {
            PacketPlayOutEntityHandle.PacketPlayOutRelEntityMoveHandle handle = createNew();
            handle.setEntityId(entityId);
            handle.setDeltaX(dx);
            handle.setDeltaY(dy);
            handle.setDeltaZ(dz);
            handle.setOnGround(onGround);
            return handle;
        }
        </code>
    }

    class PacketPlayOutEntity.PacketPlayOutRelEntityMoveLook extends PacketPlayOutEntity {
        public static (PacketPlayOutEntityHandle.PacketPlayOutRelEntityMoveLookHandle) PacketPlayOutEntity.PacketPlayOutRelEntityMoveLook createNew() {
#if version >= 1.20.5
            return new PacketPlayOutEntity$PacketPlayOutRelEntityMoveLook(0, (short) 0, (short) 0, (short) 0, (byte) 0, (byte) 0, false);
#elseif version >= 1.18
            return PacketPlayOutEntity$PacketPlayOutRelEntityMoveLook.read(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#elseif version >= 1.17
            return PacketPlayOutEntity$PacketPlayOutRelEntityMoveLook.b(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#else
            return new PacketPlayOutEntity$PacketPlayOutRelEntityMoveLook();
#endif
        }

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

        public static PacketPlayOutEntityHandle.PacketPlayOutRelEntityMoveLookHandle createNew(int entityId, double dx, double dy, double dz, float yaw, float pitch, boolean onGround) {
            PacketPlayOutEntityHandle.PacketPlayOutRelEntityMoveLookHandle handle = createNew();
            handle.setEntityId(entityId);
            handle.setDeltaX(dx);
            handle.setDeltaY(dy);
            handle.setDeltaZ(dz);
            handle.setYaw(yaw);
            handle.setPitch(pitch);
            handle.setOnGround(onGround);
            return handle;
        }
        </code>
    }
}

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

    public static optional (PacketPlayOutEntityHeadRotationHandle) PacketPlayOutEntityHeadRotation createNewEmpty() {
#if version >= 1.20.5
        #require net.minecraft.network.protocol.game.PacketPlayOutEntityHeadRotation PacketPlayOutEntityHeadRotation createHeadRotationPacket:<init>(net.minecraft.network.PacketDataSerializer serializer);
        return #createHeadRotationPacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#elseif version >= 1.17
        return new PacketPlayOutEntityHeadRotation(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#else
        return new PacketPlayOutEntityHeadRotation();
#endif
    }

    <code>
    public static PacketPlayOutEntityHeadRotationHandle createNew() {
        return T.createNewEmpty.invoke();
    }

    public static PacketPlayOutEntityHeadRotationHandle createNew(int entityId, float headYaw) {
        PacketPlayOutEntityHeadRotationHandle packet = createNew();
        packet.setEntityId(entityId);
        packet.setHeadYaw(headYaw);
        return packet;
    }
    </code>

    public static (PacketPlayOutEntityHeadRotationHandle) PacketPlayOutEntityHeadRotation createNew((org.bukkit.entity.Entity) Entity entity, float headYaw) {
        return new PacketPlayOutEntityHeadRotation(entity, (byte) com.bergerkiller.bukkit.common.utils.MathUtil.floor(headYaw * 256.0f / 360.0f));
    }

#if version >= 1.17
    #require net.minecraft.network.protocol.game.PacketPlayOutEntityHeadRotation private byte headYaw:yHeadRot;
#else
    #require net.minecraft.network.protocol.game.PacketPlayOutEntityHeadRotation private byte headYaw:b;
#endif

    public float getHeadYaw() {
        return (float) instance#headYaw * 360.0f / 256.0f;   
    }

    public void setHeadYaw(float headYaw) {
        instance#headYaw = (byte) com.bergerkiller.bukkit.common.utils.MathUtil.floor(headYaw * 256.0f / 360.0f);
    }
}

class PacketPlayInFlying extends Packet {
    protected double x;
    protected double y;
    protected double z;
#if version >= 1.17
    protected float yaw:yRot;
    protected float pitch:xRot;
    protected boolean onGround;
    protected boolean hasPos;
    protected boolean hasLook:hasRot;
#else
    protected float yaw;
    protected float pitch;
    protected boolean onGround:f;
    protected boolean hasPos;
    protected boolean hasLook;
#endif
}

// Since Minecraft 1.21.2, and only used with the new minecart movement behavior feature
optional class ClientboundMoveMinecartPacket extends Packet {
    public int getEntityId:entityId();
    public (List<NewMinecartBehaviorHandle.LerpStepHandle>) List<NewMinecartBehavior.LerpStep> getLerpSteps:lerpSteps();

    public static (ClientboundMoveMinecartPacketHandle) ClientboundMoveMinecartPacket createNew(int entityId, (List<NewMinecartBehaviorHandle.LerpStepHandle>) List<NewMinecartBehavior.LerpStep> lerpSteps) {
        return new ClientboundMoveMinecartPacket(entityId, lerpSteps);
    }
}
