package net.minecraft.network.protocol.game;

import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.ChatComponentText;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.world.scores.criteria.ObjectiveCriteria;
import net.minecraft.world.scores.criteria.ObjectiveCriteria.RenderType;
import net.minecraft.world.scores.Team$Visibility;
import net.minecraft.world.scores.Team.Visibility;
import net.minecraft.world.scores.Team$CollisionRule;
import net.minecraft.world.scores.Team.CollisionRule;
import net.minecraft.world.scores.TeamColor;
import net.minecraft.server.ServerScoreboard;
import net.minecraft.server.ServerScoreboard$Method;

import org.bukkit.scoreboard.DisplaySlot;

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

import com.bergerkiller.generated.net.minecraft.network.protocol.game.ClientboundResetScorePacketHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.ClientboundSetScorePacketHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacketHandle;

class ClientboundSetPlayerTeamPacket extends Packet {
    <code>
    public static final int METHOD_ADD = 0;
    public static final int METHOD_REMOVE = 1;
    public static final int METHOD_CHANGE = 2;
    public static final int METHOD_JOIN = 3;
    public static final int METHOD_LEAVE = 4;
    </code>

#if version >= 1.17
    #bootstrap com.bergerkiller.bukkit.common.internal.CommonBootstrap.initServer();
#endif

#if version >= 1.17
    private final int method;
#endif

#if version >= 1.17
    private String name;
#else
    private String name:a;
#endif

#if version >= 1.17
    private final Collection<String> players;
    // method field moved to the top
#elseif version >= 1.13
    private final Collection<String> players:h;
    private int method:i;
#elseif version >= 1.9
    private final Collection<String> players:h;
    private int method:i;
#else
    private final Collection<String> players:g;
    private int method:h;
#endif

    public static (ClientboundSetPlayerTeamPacketHandle) ClientboundSetPlayerTeamPacket createNew() {
#if version >= 1.20.5
        #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket private ClientboundSetPlayerTeamPacket createTeamPacket:<init>(String s, int i, java.util.Optional<ClientboundSetPlayerTeamPacket.Parameters> opt, java.util.Collection<String> collection);
        return #createTeamPacket("", 0, java.util.Optional.empty(), java.util.Collections.emptyList());
#elseif version >= 1.17
        ClientboundSetPlayerTeamPacket packet = new ClientboundSetPlayerTeamPacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
        ClientboundSetPlayerTeamPacketHandle.T.players.set(packet, Collections.emptyList());
        return packet;
#else
        return new ClientboundSetPlayerTeamPacket();
#endif
    }

#if version >= 1.17
    // All properties grouped in a container class

    // Pre-26.2 we have to access all of the fields using reflection
  #if version < 26.2
        #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket.Parameters private final Component displayName;
        #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket.Parameters private final Component prefix:playerPrefix;
        #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket.Parameters private final Component suffix:playerSuffix;
    #if version >= 1.21.5
        #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket.Parameters private final net.minecraft.world.scores.Team.Visibility visibility:nametagVisibility;
        #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket.Parameters private final net.minecraft.world.scores.Team.CollisionRule collisionRule;
    #else
        #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket.Parameters private final String visibility:nametagVisibility;
        #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket.Parameters private final String collisionRule;
    #endif
        #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket.Parameters private final ChatFormatting color;
        #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket.Parameters private final int teamOptionFlags:options;
  #endif

    #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket private final Optional<ClientboundSetPlayerTeamPacket.Parameters> parameters;
    #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket public void setParameters(ClientboundSetPlayerTeamPacket.Parameters parameters) {
        instance#parameters = java.util.Optional.of(parameters);
    }
    #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket public ClientboundSetPlayerTeamPacket.Parameters getParameters() {
        Optional opt = instance#parameters;
        if (opt == null) {
            return null;
        } else {
            return (ClientboundSetPlayerTeamPacket$Parameters) opt.orElse(null);
        }
    }
    #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket public ClientboundSetPlayerTeamPacket.Parameters getParametersOrDefault() {
        // Get existing
        Optional opt = instance#parameters;
        if (opt != null && opt.isPresent()) {
            return (ClientboundSetPlayerTeamPacket$Parameters) opt.get();
        }

        // Create new parameters with default values
        // This is a little wasteful as right after this, the parameters will be re-created with the requested property changed
        // But this keeps the API somewhat clean.
  #if version >= 26.2
        // It's a record class, so this must be all be created in one go now
        return new ClientboundSetPlayerTeamPacket$Parameters(
                Component.empty(), // displayName
                Component.empty(), // playerPrefix
                Component.empty(), // playerSuffix
                Team$Visibility.ALWAYS, // nameTagVisibility
                Team$CollisionRule.ALWAYS, // collisionRule
                java.util.Optional.empty(), // color
                (byte)0 // options
        );
  #else
    #if version >= 1.20.5
        // It does a bunch of byte stream reading now for NBT, which is incompatible with our null serializer
        // Create a new scoreboard team, which seems to work ok. Will need a null instantiator otherwise though.
        ClientboundSetPlayerTeamPacket$Parameters parameters = new ClientboundSetPlayerTeamPacket$Parameters(new net.minecraft.world.scores.PlayerTeam((net.minecraft.world.scores.Scoreboard) null, "unset"));
    #else
        ClientboundSetPlayerTeamPacket$Parameters parameters = new ClientboundSetPlayerTeamPacket$Parameters(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
    #endif
    #if version >= 1.19
        parameters#displayName = Component.empty();
        parameters#prefix = Component.empty();
        parameters#suffix = Component.empty();
    #else
        parameters#displayName = ChatComponentText.EMPTY;
        parameters#prefix = ChatComponentText.EMPTY;
        parameters#suffix = ChatComponentText.EMPTY;
    #endif

    #if version >= 1.21.5
        parameters#visibility = Team$Visibility.ALWAYS;
        parameters#collisionRule = Team$CollisionRule.ALWAYS;
    #else
        parameters#visibility = Team$Visibility.ALWAYS.name;
        parameters#collisionRule = Team$CollisionRule.ALWAYS.name;
    #endif
        parameters#color = ChatFormatting.RESET;
        parameters#teamOptionFlags = 0;

        // Also set it, so we don't have to create it again when changing the property
        instance#parameters = java.util.Optional.of(parameters);

        return parameters;
  #endif
    }

    public (ChatText) Component getDisplayName() {
        ClientboundSetPlayerTeamPacket$Parameters parameters = instance#getParameters();
        if (parameters == null) {
#if version >= 1.19
            return Component.empty();
#else
            return ChatComponentText.EMPTY;
#endif
        } else {
#if version >= 26.2
            return parameters.displayName();
#else
            return parameters#displayName;
#endif
        }
    }

    public void setDisplayName((ChatText) Component displayName) {
        ClientboundSetPlayerTeamPacket$Parameters parameters = instance#getParametersOrDefault();
#if version >= 26.2
        #require ClientboundSetPlayerTeamPacket.Parameters ClientboundSetPlayerTeamPacket.Parameters change_display_name:<record_changer>(Component displayName);
        parameters = parameters#change_display_name(displayName);
        instance#setParameters(parameters);
#else
        parameters#displayName = displayName;
#endif
    }

    public (ChatText) Component getPrefix() {
        ClientboundSetPlayerTeamPacket$Parameters parameters = instance#getParameters();
        if (parameters == null) {
#if version >= 1.19
            return Component.empty();
#else
            return ChatComponentText.EMPTY;
#endif
        } else {
#if version >= 26.2
            return parameters.playerPrefix();
#else
            return parameters#prefix;
#endif
        }
    }

    public void setPrefix((ChatText) Component prefix) {
        ClientboundSetPlayerTeamPacket$Parameters parameters = instance#getParametersOrDefault();
#if version >= 26.2
        #require ClientboundSetPlayerTeamPacket.Parameters ClientboundSetPlayerTeamPacket.Parameters change_prefix:<record_changer>(Component playerPrefix);
        parameters = parameters#change_prefix(prefix);
        instance#setParameters(parameters);
#else
        parameters#prefix = prefix;
#endif
    }

    public (ChatText) Component getSuffix() {
        ClientboundSetPlayerTeamPacket$Parameters parameters = instance#getParameters();
        if (parameters == null) {
#if version >= 1.19
            return Component.empty();
#else
            return ChatComponentText.EMPTY;
#endif
        } else {
#if version >= 26.2
            return parameters.playerSuffix();
#else
            return parameters#suffix;
#endif
        }
    }

    public void setSuffix((ChatText) Component suffix) {
        ClientboundSetPlayerTeamPacket$Parameters parameters = instance#getParametersOrDefault();
#if version >= 26.2
        #require ClientboundSetPlayerTeamPacket.Parameters ClientboundSetPlayerTeamPacket.Parameters change_suffix:<record_changer>(Component playerSuffix);
        parameters = parameters#change_suffix(suffix);
        instance#setParameters(parameters);
#else
        parameters#suffix = suffix;
#endif
    }

#if version >= 1.21.5
    // Field is of type Team.Visibility / Team.CollisionRule, not String

    public (String) Team.Visibility getVisibility() {
        ClientboundSetPlayerTeamPacket$Parameters parameters = instance#getParameters();
        if (parameters == null) {
            return Team$Visibility.ALWAYS;
        } else {
  #if version >= 26.2
            return parameters.nameTagVisibility();
  #else
            return parameters#visibility;
  #endif
        }
    }

    public void setVisibility((String) Team.Visibility visibility) {
        ClientboundSetPlayerTeamPacket$Parameters parameters = instance#getParametersOrDefault();
  #if version >= 26.2
        #require ClientboundSetPlayerTeamPacket.Parameters ClientboundSetPlayerTeamPacket.Parameters change_visibility:<record_changer>(Team.Visibility nameTagVisibility);
        parameters = parameters#change_visibility(visibility);
        instance#setParameters(parameters);
  #else
        parameters#visibility = visibility;
  #endif
    }

    public (String) Team.CollisionRule getCollisionRule() {
        ClientboundSetPlayerTeamPacket$Parameters parameters = instance#getParameters();
        if (parameters == null) {
            return Team$CollisionRule.ALWAYS;
        } else {
  #if version >= 26.2
            return parameters.collisionRule();
  #else
            return parameters#collisionRule;
  #endif
        }
    }

    public void setCollisionRule((String) Team.CollisionRule rule) {
        ClientboundSetPlayerTeamPacket$Parameters parameters = instance#getParametersOrDefault();
  #if version >= 26.2
        #require ClientboundSetPlayerTeamPacket.Parameters ClientboundSetPlayerTeamPacket.Parameters change_collision_rule:<record_changer>(Team.CollisionRule collisionRule);
        parameters = parameters#change_collision_rule(rule);
        instance#setParameters(parameters);
  #else
        parameters#collisionRule = rule;
  #endif
    }

#else
    // Field is of type String

    public (String) String getVisibility() {
        ClientboundSetPlayerTeamPacket$Parameters parameters = instance#getParameters();
        if (parameters == null) {
            return Team$Visibility.ALWAYS.name;
        } else {
            return parameters#visibility;
        }
    }

    public void setVisibility((String) String visibility) {
        ClientboundSetPlayerTeamPacket$Parameters parameters = instance#getParametersOrDefault();
        parameters#visibility = visibility;
    }

    public (String) String getCollisionRule() {
        ClientboundSetPlayerTeamPacket$Parameters parameters = instance#getParameters();
        if (parameters == null) {
            return Team$CollisionRule.ALWAYS.name;
        } else {
            return parameters#collisionRule;
        }
    }

    public void setCollisionRule((String) String rule) {
        ClientboundSetPlayerTeamPacket$Parameters parameters = instance#getParametersOrDefault();
        parameters#collisionRule = rule;
    }
#endif

#if version >= 26.2
    public (org.bukkit.ChatColor) TeamColor getColor() {
        ClientboundSetPlayerTeamPacket$Parameters parameters = instance#getParameters();
        if (parameters == null) {
            return null;
        } else {
            return (TeamColor) parameters.color().orElse(null);
        }
    }

    public void setColor((org.bukkit.ChatColor) TeamColor color) {
        ClientboundSetPlayerTeamPacket$Parameters parameters = instance#getParametersOrDefault();
        #require ClientboundSetPlayerTeamPacket.Parameters ClientboundSetPlayerTeamPacket.Parameters change_color:<record_changer>(Optional<TeamColor> color);
        parameters = parameters#change_color(java.util.Optional.ofNullable(color));
        instance#setParameters(parameters);
    }
#else
    public (org.bukkit.ChatColor) ChatFormatting getColor() {
        ClientboundSetPlayerTeamPacket$Parameters parameters = instance#getParameters();
        if (parameters == null) {
            return null;
        } else {
            return parameters#color;
        }
    }

    public void setColor((org.bukkit.ChatColor) ChatFormatting color) {
        ClientboundSetPlayerTeamPacket$Parameters parameters = instance#getParametersOrDefault();
        parameters#color = color;
    }
#endif

    public int getTeamOptionFlags() {
        ClientboundSetPlayerTeamPacket$Parameters parameters = instance#getParameters();
        if (parameters == null) {
            return 0;
        } else {
#if version >= 26.2
            return (int) parameters.options();
#else
            return (int) parameters#teamOptionFlags;
#endif
        }
    }

    public void setTeamOptionFlags(int teamOptionFlags) {
        ClientboundSetPlayerTeamPacket$Parameters parameters = instance#getParametersOrDefault();
  #if version >= 26.2
        #require ClientboundSetPlayerTeamPacket.Parameters ClientboundSetPlayerTeamPacket.Parameters change_options:<record_changer>(byte options);
        parameters = parameters#change_options((byte)teamOptionFlags);
        instance#setParameters(parameters);
  #else
        parameters#teamOptionFlags = teamOptionFlags;
  #endif
    }
#else
    // Each property was a field inside the packet

  #if version >= 1.13
    #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket private net.minecraft.network.chat.Component displayName:b;
    #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket private net.minecraft.network.chat.Component prefix:c;
    #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket private net.minecraft.network.chat.Component suffix:d;
    #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket private net.minecraft.ChatFormatting color:g;

    public (ChatText) Component getDisplayName() {
        return instance#displayName;
    }

    public void setDisplayName((ChatText) Component displayName) {
        instance#displayName = displayName;
    }

    public (ChatText) Component getPrefix() {
        return instance#prefix;
    }

    public void setPrefix((ChatText) Component prefix) {
        instance#prefix = prefix;
    }

    public (ChatText) Component getSuffix() {
        return instance#suffix;
    }

    public void setSuffix((ChatText) Component suffix) {
        instance#suffix = suffix;
    }

    public (org.bukkit.ChatColor) ChatFormatting getColor() {
        return instance#color;
    }

    public void setColor((org.bukkit.ChatColor) ChatFormatting color) {
        instance#color = color;
    }
  #else
    #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket private String displayName:b;
    #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket private String prefix:c;
    #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket private String suffix:d;
    #if version >= 1.9
      #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket private int color:g;
    #else
      #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket private int color:f;
    #endif

    public (ChatText) String getDisplayName() {
        return instance#displayName;
    }

    public void setDisplayName((ChatText) String displayName) {
        instance#displayName = displayName;
    }

    public (ChatText) String getPrefix() {
        return instance#prefix;
    }

    public void setPrefix((ChatText) String prefix) {
        instance#prefix = prefix;
    }

    public (ChatText) String getSuffix() {
        return instance#suffix;
    }

    public void setSuffix((ChatText) String suffix) {
        instance#suffix = suffix;
    }

    public (org.bukkit.ChatColor) int getColor() {
        return instance#color;
    }

    public void setColor((org.bukkit.ChatColor) int color) {
        instance#color = color;
    }
  #endif

  #if version >= 1.9
    #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket private String collisionRule:f;

    public (String) String getCollisionRule() {
        return instance#collisionRule;
    }

    public void setCollisionRule((String) String rule) {
        instance#collisionRule = rule;
    }
  #else
    public (String) String getCollisionRule() {
        return "always";
    }

    public void setCollisionRule((String) String rule) {
    }
  #endif

    #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket private String visibility:e;
  #if version >= 1.9
    #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket private int teamOptionFlags:j;
  #else
    #require net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket private int teamOptionFlags:i;
  #endif

    public (String) String getVisibility() {
        return instance#visibility;
    }

    public void setVisibility((String) String visibility) {
        instance#visibility = visibility;
    }

    public int getTeamOptionFlags() {
        return instance#teamOptionFlags;
    }

    public void setTeamOptionFlags(int teamOptionFlags) {
        instance#teamOptionFlags = teamOptionFlags;
    }
#endif
}

// Note: same as ClientboundSetScorePacket on MC 1.20.2 and before
// It uses an 'action' field to change which type of packet is represented
class ClientboundResetScorePacket extends Packet {
#if version >= 1.17
    private String name:owner;
    private String objName:objectiveName;
#else
    private String name:a;
    private String objName:b;
#endif

    public static (ClientboundResetScorePacketHandle) ClientboundResetScorePacket createNew(String name, String objectiveName) {
#if version >= 1.20.3
        return new ClientboundResetScorePacket(name, objectiveName);
#elseif version >= 1.13
        return new ClientboundResetScorePacket(ServerScoreboard$Method.REMOVE, objectiveName, name, 0);
#else
        ClientboundResetScorePacket packet = new ClientboundResetScorePacket(name);
        #require ClientboundResetScorePacket private String objName:b;
        packet#objName = objectiveName;
        return packet;
#endif
    }
}

class ClientboundSetScorePacket extends Packet {
#if version >= 1.17
    private String name:owner;
    private String objName:objectiveName;
    private int value:score;
#else
    private String name:a;
    private String objName:b;
    private int value:c;
#endif

    public static (ClientboundSetScorePacketHandle) ClientboundSetScorePacket createNew(String name, String objectiveName, int score) {
#if version >= 1.20.3
        //TODO: Display and number format
        return new ClientboundSetScorePacket(name, objectiveName, score, null, null);
#elseif version >= 1.13
        return new ClientboundSetScorePacket(ServerScoreboard$Method.CHANGE, objectiveName, name, score);
#else
        ClientboundSetScorePacket packet = new ClientboundSetScorePacket();
        #require ClientboundSetScorePacket private String scorePacketName:a;
        #require ClientboundSetScorePacket private String scorePacketObjName:b;
        #require ClientboundSetScorePacket private int scorePacketValue:c;
        #require ClientboundSetScorePacket private net.minecraft.server.ServerScoreboard.Method scorePacketAction:d;
        packet#scorePacketName = name;
        packet#scorePacketObjName = objectiveName;
        packet#scorePacketValue = score;
        packet#scorePacketAction = ServerScoreboard$Method.CHANGE;
        return packet;
#endif
    }
}

class ClientboundSetObjectivePacket extends Packet {
#if version >= 1.17
    private String name:objectiveName;
    private (ChatText) Component displayName;
    private (Object) ObjectiveCriteria.RenderType criteria:renderType;
    private int action:method;
#elseif version >= 1.13
    private String name:a;
    private (ChatText) Component displayName:b;
    private (Object) ObjectiveCriteria.RenderType criteria:c;
    private int action:d;
#else
    private String name:a;
    private (ChatText) String displayName:b;
    private (Object) ObjectiveCriteria.RenderType criteria:c;
    private int action:d;
#endif
}

class ClientboundSetDisplayObjectivePacket extends Packet {
#if version >= 1.20.2
    private final (DisplaySlot) net.minecraft.world.scores.DisplaySlot display:slot;
    private String name:objectiveName;
#elseif version >= 1.17
    private (DisplaySlot) int display:slot;
    private String name:objectiveName;
#else
    private (DisplaySlot) int display:a;
    private String name:b;
#endif
}
