package net.minecraft.world.scores;

import java.util.AbstractList;
import net.minecraft.nbt.CompoundTag;
import java.util.function.Function;
import net.minecraft.nbt.ListTag;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.Entity;
import net.minecraft.ChatFormatting;
import java.util.Iterator;
import java.util.Comparator;
import java.util.Collection;
import java.util.Collections;
import java.util.function.Consumer;
import com.google.common.collect.Lists;
import net.minecraft.network.chat.Component;
import javax.annotation.Nullable;
import com.google.common.collect.Maps;
import java.util.List;
import net.minecraft.world.scores.criteria.ObjectiveCriteria;
import java.util.Map;

public class Scoreboard {
    private final Map<String, Objective> objectivesByName;
    private final Map<ObjectiveCriteria, List<Objective>> objectivesByCriteria;
    private final Map<String, Map<Objective, Score>> playerScores;
    private final Objective[] displayObjectives;
    private final Map<String, PlayerTeam> teamsByName;
    private final Map<String, PlayerTeam> teamsByPlayer;
    private static String[] displaySlotNames;
    
    public Scoreboard() {
        this.objectivesByName = Maps.newHashMap();
        this.objectivesByCriteria = Maps.newHashMap();
        this.playerScores = Maps.newHashMap();
        this.displayObjectives = new Objective[19];
        this.teamsByName = Maps.newHashMap();
        this.teamsByPlayer = Maps.newHashMap();
    }
    
    public boolean hasObjective(final String string) {
        return this.objectivesByName.containsKey(string);
    }
    
    public Objective getOrCreateObjective(final String string) {
        return this.objectivesByName.get(string);
    }
    
    @Nullable
    public Objective getObjective(@Nullable final String string) {
        return this.objectivesByName.get(string);
    }
    
    public Objective addObjective(final String string, final ObjectiveCriteria cwo, final Component lf, final ObjectiveCriteria.RenderType a) {
        if (string.length() > 16) {
            throw new IllegalArgumentException("The objective name '" + string + "' is too long!");
        }
        if (this.objectivesByName.containsKey(string)) {
            throw new IllegalArgumentException("An objective with the name '" + string + "' already exists!");
        }
        final Objective cwi6 = new Objective(this, string, cwo, lf, a);
        this.objectivesByCriteria.computeIfAbsent(cwo, cwo -> Lists.newArrayList()).add(cwi6);
        this.objectivesByName.put(string, cwi6);
        this.onObjectiveAdded(cwi6);
        return cwi6;
    }
    
    public final void forAllObjectives(final ObjectiveCriteria cwo, final String string, final Consumer<Score> consumer) {
        this.objectivesByCriteria.getOrDefault(cwo, Collections.<Objective>emptyList()).forEach(cwi -> consumer.accept(this.getOrCreatePlayerScore(string, cwi)));
    }
    
    public boolean hasPlayerScore(final String string, final Objective cwi) {
        final Map<Objective, Score> map4 = this.playerScores.get(string);
        if (map4 == null) {
            return false;
        }
        final Score cwk5 = map4.get(cwi);
        return cwk5 != null;
    }
    
    public Score getOrCreatePlayerScore(final String string, final Objective cwi) {
        if (string.length() > 40) {
            throw new IllegalArgumentException("The player name '" + string + "' is too long!");
        }
        final Map<Objective, Score> map4 = this.playerScores.computeIfAbsent(string, string -> Maps.newHashMap());
        final Score cwk4;
        return map4.computeIfAbsent(cwi, cwi -> {
            cwk4 = new Score(this, cwi, string);
            cwk4.setScore(0);
            return cwk4;
        });
    }
    
    public Collection<Score> getPlayerScores(final Objective cwi) {
        final List<Score> list3 = Lists.newArrayList();
        for (final Map<Objective, Score> map5 : this.playerScores.values()) {
            final Score cwk6 = map5.get(cwi);
            if (cwk6 != null) {
                list3.add(cwk6);
            }
        }
        list3.sort(Score.SCORE_COMPARATOR);
        return list3;
    }
    
    public Collection<Objective> getObjectives() {
        return this.objectivesByName.values();
    }
    
    public Collection<String> getObjectiveNames() {
        return this.objectivesByName.keySet();
    }
    
    public Collection<String> getTrackedPlayers() {
        return Lists.newArrayList(this.playerScores.keySet());
    }
    
    public void resetPlayerScore(final String string, @Nullable final Objective cwi) {
        if (cwi == null) {
            final Map<Objective, Score> map4 = this.playerScores.remove(string);
            if (map4 != null) {
                this.onPlayerRemoved(string);
            }
        }
        else {
            final Map<Objective, Score> map4 = this.playerScores.get(string);
            if (map4 != null) {
                final Score cwk5 = map4.remove(cwi);
                if (map4.size() < 1) {
                    final Map<Objective, Score> map5 = this.playerScores.remove(string);
                    if (map5 != null) {
                        this.onPlayerRemoved(string);
                    }
                }
                else if (cwk5 != null) {
                    this.onPlayerScoreRemoved(string, cwi);
                }
            }
        }
    }
    
    public Map<Objective, Score> getPlayerScores(final String string) {
        Map<Objective, Score> map3 = this.playerScores.get(string);
        if (map3 == null) {
            map3 = Maps.newHashMap();
        }
        return map3;
    }
    
    public void removeObjective(final Objective cwi) {
        this.objectivesByName.remove(cwi.getName());
        for (int integer3 = 0; integer3 < 19; ++integer3) {
            if (this.getDisplayObjective(integer3) == cwi) {
                this.setDisplayObjective(integer3, null);
            }
        }
        final List<Objective> list3 = this.objectivesByCriteria.get(cwi.getCriteria());
        if (list3 != null) {
            list3.remove(cwi);
        }
        for (final Map<Objective, Score> map5 : this.playerScores.values()) {
            map5.remove(cwi);
        }
        this.onObjectiveRemoved(cwi);
    }
    
    public void setDisplayObjective(final int integer, @Nullable final Objective cwi) {
        this.displayObjectives[integer] = cwi;
    }
    
    @Nullable
    public Objective getDisplayObjective(final int integer) {
        return this.displayObjectives[integer];
    }
    
    public PlayerTeam getPlayerTeam(final String string) {
        return this.teamsByName.get(string);
    }
    
    public PlayerTeam addPlayerTeam(final String string) {
        if (string.length() > 16) {
            throw new IllegalArgumentException("The team name '" + string + "' is too long!");
        }
        PlayerTeam cwj3 = this.getPlayerTeam(string);
        if (cwj3 != null) {
            throw new IllegalArgumentException("A team with the name '" + string + "' already exists!");
        }
        cwj3 = new PlayerTeam(this, string);
        this.teamsByName.put(string, cwj3);
        this.onTeamAdded(cwj3);
        return cwj3;
    }
    
    public void removePlayerTeam(final PlayerTeam cwj) {
        this.teamsByName.remove(cwj.getName());
        for (final String string4 : cwj.getPlayers()) {
            this.teamsByPlayer.remove(string4);
        }
        this.onTeamRemoved(cwj);
    }
    
    public boolean addPlayerToTeam(final String string, final PlayerTeam cwj) {
        if (string.length() > 40) {
            throw new IllegalArgumentException("The player name '" + string + "' is too long!");
        }
        if (this.getPlayersTeam(string) != null) {
            this.removePlayerFromTeam(string);
        }
        this.teamsByPlayer.put(string, cwj);
        return cwj.getPlayers().add(string);
    }
    
    public boolean removePlayerFromTeam(final String string) {
        final PlayerTeam cwj3 = this.getPlayersTeam(string);
        if (cwj3 != null) {
            this.removePlayerFromTeam(string, cwj3);
            return true;
        }
        return false;
    }
    
    public void removePlayerFromTeam(final String string, final PlayerTeam cwj) {
        if (this.getPlayersTeam(string) != cwj) {
            throw new IllegalStateException("Player is either on another team or not on any team. Cannot remove from team '" + cwj.getName() + "'.");
        }
        this.teamsByPlayer.remove(string);
        cwj.getPlayers().remove(string);
    }
    
    public Collection<String> getTeamNames() {
        return this.teamsByName.keySet();
    }
    
    public Collection<PlayerTeam> getPlayerTeams() {
        return this.teamsByName.values();
    }
    
    @Nullable
    public PlayerTeam getPlayersTeam(final String string) {
        return this.teamsByPlayer.get(string);
    }
    
    public void onObjectiveAdded(final Objective cwi) {
    }
    
    public void onObjectiveChanged(final Objective cwi) {
    }
    
    public void onObjectiveRemoved(final Objective cwi) {
    }
    
    public void onScoreChanged(final Score cwk) {
    }
    
    public void onPlayerRemoved(final String string) {
    }
    
    public void onPlayerScoreRemoved(final String string, final Objective cwi) {
    }
    
    public void onTeamAdded(final PlayerTeam cwj) {
    }
    
    public void onTeamChanged(final PlayerTeam cwj) {
    }
    
    public void onTeamRemoved(final PlayerTeam cwj) {
    }
    
    public static String getDisplaySlotName(final int integer) {
        switch (integer) {
            case 0: {
                return "list";
            }
            case 1: {
                return "sidebar";
            }
            case 2: {
                return "belowName";
            }
            default: {
                if (integer >= 3 && integer <= 18) {
                    final ChatFormatting g2 = ChatFormatting.getById(integer - 3);
                    if (g2 != null && g2 != ChatFormatting.RESET) {
                        return "sidebar.team." + g2.getName();
                    }
                }
                return null;
            }
        }
    }
    
    public static int getDisplaySlotByName(final String string) {
        if ("list".equalsIgnoreCase(string)) {
            return 0;
        }
        if ("sidebar".equalsIgnoreCase(string)) {
            return 1;
        }
        if ("belowName".equalsIgnoreCase(string)) {
            return 2;
        }
        if (string.startsWith("sidebar.team.")) {
            final String string2 = string.substring("sidebar.team.".length());
            final ChatFormatting g3 = ChatFormatting.getByName(string2);
            if (g3 != null && g3.getId() >= 0) {
                return g3.getId() + 3;
            }
        }
        return -1;
    }
    
    public static String[] getDisplaySlotNames() {
        if (Scoreboard.displaySlotNames == null) {
            Scoreboard.displaySlotNames = new String[19];
            for (int integer1 = 0; integer1 < 19; ++integer1) {
                Scoreboard.displaySlotNames[integer1] = getDisplaySlotName(integer1);
            }
        }
        return Scoreboard.displaySlotNames;
    }
    
    public void entityRemoved(final Entity akq) {
        if (akq == null || akq instanceof Player || akq.isAlive()) {
            return;
        }
        final String string3 = akq.getStringUUID();
        this.resetPlayerScore(string3, null);
        this.removePlayerFromTeam(string3);
    }
    
    protected ListTag savePlayerScores() {
        final ListTag jz2 = new ListTag();
        final CompoundTag jt3;
        final AbstractList<CompoundTag> list;
        this.playerScores.values().stream().map(Map::values).forEach(collection -> collection.stream().filter(cwk -> cwk.getObjective() != null).forEach(cwk -> {
            jt3 = new CompoundTag();
            jt3.putString("Name", cwk.getOwner());
            jt3.putString("Objective", cwk.getObjective().getName());
            jt3.putInt("Score", cwk.getScore());
            jt3.putBoolean("Locked", cwk.isLocked());
            list.add(jt3);
        }));
        return jz2;
    }
    
    protected void loadPlayerScores(final ListTag jz) {
        for (int integer3 = 0; integer3 < jz.size(); ++integer3) {
            final CompoundTag jt4 = jz.getCompound(integer3);
            final Objective cwi5 = this.getOrCreateObjective(jt4.getString("Objective"));
            String string6 = jt4.getString("Name");
            if (string6.length() > 40) {
                string6 = string6.substring(0, 40);
            }
            final Score cwk7 = this.getOrCreatePlayerScore(string6, cwi5);
            cwk7.setScore(jt4.getInt("Score"));
            if (jt4.contains("Locked")) {
                cwk7.setLocked(jt4.getBoolean("Locked"));
            }
        }
    }
}
