package net.minecraft.network.protocol.game;

import net.minecraft.network.protocol.Packet;
import net.minecraft.world.entity.PositionMoveRotation;
import net.minecraft.world.entity.Relative;
import net.minecraft.world.phys.Vec3;

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

import com.bergerkiller.generated.net.minecraft.network.protocol.game.ClientboundPlayerPositionPacketHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.ServerboundPlayerInputPacketHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.ClientboundPlayerRotationPacketHandle;

class ClientboundPlayerPositionPacket extends Packet {
#if version >= 1.21.2
    public double getX() { return instance.change().position().x; }
    public double getY() { return instance.change().position().y; }
    public double getZ() { return instance.change().position().z; }
    public float getYaw() { return instance.change().yRot(); }
    public float getPitch() { return instance.change().xRot(); }
    public (RelativeFlags) Set<Relative> getRelativeFlags:relatives();
    public int getTeleportWaitTimer:id();
#elseif version >= 1.18
    public double getX();
    public double getY();
    public double getZ();
    public float getYaw:getYRot();
    public float getPitch:getXRot();
    public (RelativeFlags) Set<Relative> getRelativeFlags:getRelativeArguments();
    public int getTeleportWaitTimer:getId();
#elseif version >= 1.17
    public double getX:b();
    public double getY:c();
    public double getZ:d();
    public float getYaw:e();
    public float getPitch:f();
    public (RelativeFlags) Set<Relative> getRelativeFlags:i();
    public int getTeleportWaitTimer:g();
#else
    #require ClientboundPlayerPositionPacket private double x:a;
    #require ClientboundPlayerPositionPacket private double y:b;
    #require ClientboundPlayerPositionPacket private double z:c;
    #require ClientboundPlayerPositionPacket private float yaw:d;
    #require ClientboundPlayerPositionPacket private float pitch:e;
    #require ClientboundPlayerPositionPacket private Set<Relative> relativeArguments:f;

    public double getX() { return instance#x; }
    public double getY() { return instance#y; }
    public double getZ() { return instance#z; }
    public float getYaw() { return instance#yaw; }
    public float getPitch() { return instance#pitch; }
    public (RelativeFlags) Set<Relative> getRelativeFlags() { return instance#relativeArguments; }

    public int getTeleportWaitTimer() {
  #if version >= 1.9
        #require ClientboundPlayerPositionPacket private int teleportWaitTimer:g;
        return instance#teleportWaitTimer;
  #else
        return 0;
  #endif
    }
#endif

    public static (ClientboundPlayerPositionPacketHandle) ClientboundPlayerPositionPacket createNew(double x, double y, double z, float yaw, float pitch, double deltaX, double deltaY, double deltaZ, (RelativeFlags) Set<Relative> relativeFlags, int teleportWaitTimer) {
#if version >= 1.21.2
        return ClientboundPlayerPositionPacket.of(teleportWaitTimer, new PositionMoveRotation(
            new Vec3(x, y, z), new Vec3(deltaX, deltaY, deltaZ), yaw, pitch
        ), relativeFlags);
#elseif version >= 1.19.4
        return new ClientboundPlayerPositionPacket(x, y, z, yaw, pitch, relativeFlags, teleportWaitTimer);
#elseif version >= 1.17
        return new ClientboundPlayerPositionPacket(x, y, z, yaw, pitch, relativeFlags, teleportWaitTimer, false);
#elseif version >= 1.9
        return new ClientboundPlayerPositionPacket(x, y, z, yaw, pitch, relativeFlags, teleportWaitTimer);
#else
        return new ClientboundPlayerPositionPacket(x, y, z, yaw, pitch, relativeFlags);
#endif
    }

    <code>
    public static ClientboundPlayerPositionPacketHandle createNew(double x, double y, double z, float yaw, float pitch, double deltaX, double deltaY, double deltaZ, RelativeFlags relativeFlags) {
        return createNew(x, y, z, yaw, pitch, deltaX, deltaY, deltaZ, relativeFlags, 0);
    }

    public static ClientboundPlayerPositionPacketHandle createNew(double x, double y, double z, float yaw, float pitch, RelativeFlags relativeFlags) {
        return createNew(x, y, z, yaw, pitch, 0.0, 0.0, 0.0, relativeFlags, 0);
    }

    public static ClientboundPlayerPositionPacketHandle createRelative(double dx, double dy, double dz, float dyaw, float dpitch) {
        return createNew(dx, dy, dz, dyaw, dpitch, RelativeFlags.RELATIVE_POSITION_ROTATION);
    }

    public static ClientboundPlayerPositionPacketHandle createAbsolute(org.bukkit.Location location) {
        return createAbsolute(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
    }

    public static ClientboundPlayerPositionPacketHandle createAbsolute(double x, double y, double z, float yaw, float pitch) {
        return createNew(x, y, z, yaw, pitch, RelativeFlags.ABSOLUTE_POSITION);
    }
    </code>
}

// Since Minecraft 1.21.2
// Falls back to the ClientboundPlayerPositionPacket on Minecraft versions before 1.21.2,
// making use of a relative position of 0 0 0 so only rotation is updated.
class ClientboundPlayerRotationPacket extends Packet {
#if version >= 1.21.9
    public float getYaw:yRot();
    public boolean isYawRelative:relativeY();
    public float getPitch:xRot();
    public boolean isPitchRelative:relativeX();

#elseif version >= 1.21.2
    public float getYaw:yRot();
    public boolean isYawRelative() { return false; }
    public float getPitch:xRot();
    public boolean isPitchRelative() { return false; }

#elseif version >= 1.18
    public float getYaw:getYRot();
    public float getPitch:getXRot();

    public boolean isYawRelative() {
        return instance.getRelativeArguments().contains((Object) Relative.Y_ROT);
    }
    public boolean isPitchRelative() {
        return instance.getRelativeArguments().contains((Object) Relative.X_ROT);
    }

#elseif version >= 1.17
    public float getYaw:e();
    public float getPitch:f();

    public boolean isYawRelative() {
        return instance.i().contains((Object) Relative.Y_ROT);
    }
    public boolean isPitchRelative() {
        return instance.i().contains((Object) Relative.X_ROT);
    }

#else
    #require ClientboundPlayerPositionPacket private float yaw:d;
    #require ClientboundPlayerPositionPacket private float pitch:e;
    #require ClientboundPlayerPositionPacket private Set<Relative> relativeArguments:f;

    public float getYaw() { return instance#yaw; }
    public float getPitch() { return instance#pitch; }

    public boolean isYawRelative() {
        java.util.Set relativeFlags = instance#relativeArguments;
        return relativeFlags.contains((Object) Relative.Y_ROT);
    }
    public boolean isPitchRelative() {
        java.util.Set relativeFlags = instance#relativeArguments;
        return relativeFlags.contains((Object) Relative.X_ROT);
    }
#endif

    public static boolean isRelativeSupported() {
#if version < 1.21.2 || version >= 1.21.9
        return true;
#else
        return false;
#endif
    }

    public static (ClientboundPlayerRotationPacketHandle) ClientboundPlayerRotationPacket createNew(float yaw, boolean isYawRelative, float pitch, boolean isPitchRelative) {
#if version >= 1.21.9
        return new ClientboundPlayerRotationPacket(yaw, isYawRelative, pitch, isPitchRelative);
#elseif version >= 1.21.2
        if (isYawRelative || isPitchRelative) {
            throw new UnsupportedOperationException("Relative yaw/pitch is not supported on this version of Minecraft");
        }
        return new ClientboundPlayerRotationPacket(yaw, pitch);
#else
        double x = 0.0;
        double y = 0.0;
        double z = 0.0;
        int teleportWaitTimer = 0;
        java.util.Set relativeFlags = new java.util.LinkedHashSet();
        relativeFlags.add((Object) Relative.X);
        relativeFlags.add((Object) Relative.Y);
        relativeFlags.add((Object) Relative.Z);
        if (isYawRelative) {
            relativeFlags.add((Object) Relative.Y_ROT);
        }
        if (isPitchRelative) {
            relativeFlags.add((Object) Relative.X_ROT);
        }
  #if version >= 1.19.4
        return new ClientboundPlayerPositionPacket(x, y, z, yaw, pitch, relativeFlags, teleportWaitTimer);
  #elseif version >= 1.17
        return new ClientboundPlayerPositionPacket(x, y, z, yaw, pitch, relativeFlags, teleportWaitTimer, false);
  #elseif version >= 1.9
        return new ClientboundPlayerPositionPacket(x, y, z, yaw, pitch, relativeFlags, teleportWaitTimer);
  #else
        return new ClientboundPlayerPositionPacket(x, y, z, yaw, pitch, relativeFlags);
  #endif
#endif
    }

    <code>
    public static ClientboundPlayerRotationPacketHandle createAbsolute(float yaw, float pitch) {
        return createNew(yaw, false, pitch, false);
    }

    public static ClientboundPlayerRotationPacketHandle createRelative(float yaw, float pitch) {
        return createNew(yaw, true, pitch, true);
    }
    </code>
}

class ServerboundPlayerInputPacket extends Packet {
#if version >= 1.21.2
    public static (ServerboundPlayerInputPacketHandle) ServerboundPlayerInputPacket createNew(boolean isLeft, boolean isRight, boolean isForward, boolean isBackward, boolean isJump, boolean isUnmount, boolean isSprint) {
        return new ServerboundPlayerInputPacket(new net.minecraft.world.entity.player.Input(
            isForward, isBackward, isLeft, isRight, isJump, isUnmount, isSprint));
    }

    public boolean isLeft() { return instance.input().left(); }
    public boolean isRight() { return instance.input().right(); }
    public boolean isForward() { return instance.input().forward(); }
    public boolean isBackward() { return instance.input().backward(); }

    public float getSideways() {
        net.minecraft.world.entity.player.Input input = instance.input();
        if (input.left() != input.right()) {
            return input.left() ? 0.98f : -0.98f;
        } else {
            return 0.0f;
        }
    }

    public float getForwards() {
        net.minecraft.world.entity.player.Input input = instance.input();
        if (input.forward() != input.backward()) {
            return input.forward() ? 0.98f : -0.98f;
        } else {
            return 0.0f;
        }
    }

    public boolean isJump() { return instance.input().jump(); }
    public boolean isUnmount() { return instance.input().shift(); }
    public boolean isSprint() { return instance.input().sprint(); }

#else
  #if version >= 1.17
    #require ServerboundPlayerInputPacket private final float sideways:xxa;
    #require ServerboundPlayerInputPacket private final float forwards:zza;
    #require ServerboundPlayerInputPacket private final boolean jump:isJumping;
    #require ServerboundPlayerInputPacket private final boolean unmount:isShiftKeyDown;
  #else
    #require ServerboundPlayerInputPacket private float sideways:a;
    #require ServerboundPlayerInputPacket private float forwards:b;
    #require ServerboundPlayerInputPacket private boolean jump:c;
    #require ServerboundPlayerInputPacket private boolean unmount:d;
  #endif

    public static (ServerboundPlayerInputPacketHandle) ServerboundPlayerInputPacket createNew(boolean isLeft, boolean isRight, boolean isForward, boolean isBackward, boolean isJump, boolean isUnmount, boolean isSprint) {
       float sideways = (isLeft == isRight) ? 0.0f : (isLeft ? 0.98f : -0.98f);
       float forwards = (isForward == isBackward) ? 0.0f : (isForward ? 0.98f : -0.98f);
  #if version >= 1.17
       return new ServerboundPlayerInputPacket(sideways, forwards, isJump, isUnmount);
  #else
       ServerboundPlayerInputPacket packet = new ServerboundPlayerInputPacket();
       packet#sideways = sideways;
       packet#forwards = forwards;
       packet#jump = isJump;
       packet#unmount = isUnmount;
       return packet;
  #endif
    }

    public boolean isLeft() { return instance#sideways > 0.0f; }
    public boolean isRight() { return instance#sideways < 0.0f; }
    public boolean isForward() { return instance#forwards > 0.0f; }
    public boolean isBackward() { return instance#forwards < 0.0f; }

    public float getSideways() { return instance#sideways; }
    public float getForwards() { return instance#forwards; }
    public boolean isJump() { return instance#jump; }
    public boolean isUnmount() { return instance#unmount; }
    public boolean isSprint() { return false; }
#endif
}
