package net.minecraft.network.protocol.game;

import net.minecraft.network.protocol.Packet;
import net.minecraft.world.entity.Entity;

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

// Only >= MC 1.9
optional class PacketPlayInVehicleMove extends Packet {
#if version >= 1.17
    private double posX:x;
    private double posY:y;
    private double posZ:z;
    private float yaw:yRot;
    private float pitch:xRot;
#else
    private double posX:a;
    private double posY:b;
    private double posZ:c;
    private float yaw:d;
    private float pitch:e;
#endif
    
}

// Only >= MC 1.9
optional class PacketPlayOutVehicleMove extends Packet {
#if version >= 1.17
    private double posX:x;
    private double posY:y;
    private double posZ:z;
    private float yaw:yRot;
    private float pitch:xRot;
#else
    private double posX:a;
    private double posY:b;
    private double posZ:c;
    private float yaw:d;
    private float pitch:e;
#endif
}

class PacketPlayOutEntityTeleport 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.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 optional byte yaw_raw:yRot;
    private optional byte pitch_raw:xRot;
    private boolean onGround;
#else
    private optional byte yaw_raw:e;
    private optional byte pitch_raw:f;
    private boolean onGround:g;
#endif

    public static (PacketPlayOutEntityTeleportHandle) PacketPlayOutEntityTeleport createNew() {
#if version >= 1.20.5
        #require net.minecraft.network.protocol.game.PacketPlayOutEntityTeleport private PacketPlayOutEntityTeleport createTeleportPacket:<init>(net.minecraft.network.PacketDataSerializer serializer);
        return #createTeleportPacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#elseif version >= 1.17
        return new PacketPlayOutEntityTeleport(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#else
        return new PacketPlayOutEntityTeleport();
#endif
    }

    public (PacketPlayOutEntityTeleportHandle) PacketPlayOutEntityTeleport((org.bukkit.entity.Entity) Entity entity);

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

    public static PacketPlayOutEntityTeleportHandle createNew(int entityId, double posX, double posY, double posZ, float yaw, float pitch, boolean onGround) {
        PacketPlayOutEntityTeleportHandle handle = createNew();
        handle.setEntityId(entityId);
        handle.setPosX(posX);
        handle.setPosY(posY);
        handle.setPosZ(posZ);
        handle.setYaw(yaw);
        handle.setPitch(pitch);
        handle.setOnGround(onGround);
        return handle;
    }

    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);
    }
    </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;
    private int motX_raw:xa;
    private int motY_raw:ya;
    private int motZ_raw:za;
#else
    private int entityId:a;
    private int motX_raw:b;
    private int motY_raw:c;
    private int motZ_raw:d;
#endif

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

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

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

    public double getMotX() {
        return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(T.motX_raw.getInteger(getRaw()));
    }

    public double getMotY() {
        return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(T.motY_raw.getInteger(getRaw()));
    }

    public double getMotZ() {
        return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(T.motZ_raw.getInteger(getRaw()));
    }

    public void setMotX(double motX) {
        T.motX_raw.setInteger(getRaw(), com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motX));
    }

    public void setMotY(double motY) {
        T.motY_raw.setInteger(getRaw(), com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motY));
    }

    public void setMotZ(double motZ) {
        T.motZ_raw.setInteger(getRaw(), com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motZ));
    }
    </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
}
