Hub / src / main / java / com / lifeknight / relaymchub / statistics / StatisticsLeaderboard.java
StatisticsLeaderboard.java
Raw
package com.lifeknight.relaymchub.statistics;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.lifeknight.relaymchub.Main;
import com.lifeknight.relaymchub.player.HubPlayer;
import com.lifeknight.relaymcutils.RelayMCUtils;
import com.lifeknight.relaymcutils.player.SmartPlayer;
import com.lifeknight.relaymcutils.utilities.Parkour;
import com.lifeknight.relayutils.RelayUtils;
import com.lifeknight.relayutils.basic.Miscellaneous;
import com.lifeknight.relayutils.basic.Text;
import com.lifeknight.relayutils.game.GameAction;
import com.lifeknight.relayutils.game.MainGame;
import com.lifeknight.relayutils.game.SpecificGameAction;
import com.lifeknight.relayutils.player.Group;
import com.lifeknight.relayutils.player.data.ProfileColumn;
import com.lifeknight.relayutils.utilities.ComponentBuilder;
import com.lifeknight.relayutils.utilities.Jsonable;
import com.lifeknight.relayutils.utilities.variables.SmartNumber;
import com.lifeknight.relayutils.utilities.variables.SmartString;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftEntity;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.util.BoundingBox;

import java.sql.ResultSet;
import java.util.*;

import static org.bukkit.ChatColor.*;

public class StatisticsLeaderboard extends Jsonable {
    public static final List<StatisticsLeaderboard> LEADERBOARDS = new ArrayList<>();
    public static int NEXT_ID = 0;
    public static final Map<HubPlayer, Integer> HUB_PLAYER_TO_SELECTED = new HashMap<>();

    public final int id;
    public final SmartNumber.SmartDouble x;
    public final SmartNumber.SmartDouble y;
    public final SmartNumber.SmartDouble z;
    public final SmartString statisticName;

    public ArmorStand titleArmorStand;
    public List<ArmorStand> allTimeArmorStands = new ArrayList<>();
    public List<ArmorStand> weeklyArmorStands = new ArrayList<>();
    public List<ArmorStand> dailyArmorStands = new ArrayList<>();

    public Map<UUID, Number> allTimeLeaderboard = new HashMap<>();
    public Map<UUID, Number> weeklyLeaderboard = new HashMap<>();
    public Map<UUID, Number> dailyLeaderboard = new HashMap<>();

    public Map<UUID, ArmorStand> allTimePersonal = new HashMap<>();
    public Map<UUID, ArmorStand> weeklyPersonal = new HashMap<>();
    public Map<UUID, ArmorStand> dailyPersonal = new HashMap<>();

    public final Map<UUID, Integer> playerToLeaderboardType = new HashMap<>();

    public StatisticsLeaderboard(Location location, String statistic) {
        this(location.getX(), location.getY(), location.getZ(), statistic);
    }

    public StatisticsLeaderboard(double x, double y, double z, String statistic) {
        this.x = (SmartNumber.SmartDouble) super.createNumber("X", "Location", x);
        this.y = (SmartNumber.SmartDouble) super.createNumber("Y", "Location", y);
        this.z = (SmartNumber.SmartDouble) super.createNumber("Z", "Location", z);
        this.statisticName = super.createString("Statistic", "Data", statistic);

        this.createArmorStands();

        LEADERBOARDS.add(this);
        this.id = NEXT_ID++;
    }

    public static StatisticsLeaderboard getFromStand(ArmorStand armorStand) {
        for (StatisticsLeaderboard leaderboard : LEADERBOARDS) {
            if (leaderboard.getAllArmorStands().contains(armorStand)) {
                return leaderboard;
            }
        }

        return null;
    }

    @Override
    public void fromJson(JsonObject jsonObject) {
        super.fromJson(jsonObject);

        this.removeArmorStands();

        this.createArmorStands();
    }

    public double getX() {
        return this.x.getValue();
    }

    public double getY() {
        return this.y.getValue();
    }

    public double getZ() {
        return this.z.getValue();
    }

    public ArmorStand spawnArmorStand(Location location) {
        ArmorStand armorStand = (ArmorStand) this.getWorld().spawnEntity(location, EntityType.ARMOR_STAND);
        RelayUtils.log("Spawned armor stand %d (%s)", armorStand.getEntityId(), this.getStatisticName());
        armorStand.setInvisible(true);
        armorStand.setGravity(false);
        return armorStand;
    }

    public ArmorStand spawnArmorStand() {
        return this.spawnArmorStand(this.getLocation());
    }

    public String getTitle() {
        SpecificGameAction specificGameAction = SpecificGameAction.getSpecificGameAction(this.getStatisticName());

        if (specificGameAction != null) {
            return specificGameAction.getGameType().getDisplaySuffix() + " " + specificGameAction.getGameAction().getPluralPrettyName();
        }

        for (MainGame value : MainGame.values()) {
            String pretty = Text.prettify(this.getStatisticName());
            if (pretty.contains(value.getName())) {
                return pretty.substring(value.getName().length() + 1);
            }
        }

        return Text.prettify(this.getStatisticName());
    }

    public void createArmorStands() {
        this.titleArmorStand = this.spawnArmorStand(this.getLocation().add(0, -1.5F + 0.3F * 12, 0));
        this.prepareArmorStand(this.titleArmorStand);
        GameAction gameAction = this.getGameAction();
        if (gameAction != null) {
            this.titleArmorStand.setCustomName(GOLD + gameAction.getPluralPrettyName());
        } else if (this.getSpecificGameAction() != null) {
            this.titleArmorStand.setCustomName(GOLD + this.getSpecificGameAction().getGameType().getDisplaySuffix() + " " + this.getSpecificGameAction().getGameAction().getPluralPrettyName());
        } else if (this.isParkour()) {
            Parkour parkour = Parkour.getParkour(this.getStatisticName().split("_")[1]);
            if (parkour != null) {
                this.titleArmorStand.setCustomName(GOLD + "Parkour - " + parkour.getCompositeFormattedName());
            }
        } else {
            this.titleArmorStand.setCustomName(GOLD + this.getTitle());
        }

        this.allTimeArmorStands = this.getNewArmorStands();
        this.setupTypes(this.allTimeArmorStands, 0);
        if (this.hasTimelyPositions()) {
            this.weeklyArmorStands = this.getNewArmorStands();
            this.setupTypes(this.weeklyArmorStands, 1);
            this.dailyArmorStands = this.getNewArmorStands();
            this.setupTypes(this.dailyArmorStands, 2);
        }
    }

    public void setupTypes(List<ArmorStand> armorStands, int type) {
        String title = AQUA + BOLD.toString();
        switch (type) {
            case 0 -> title += "All Time";
            case 1 -> title += "Weekly";
            case 2 -> title += "Daily";
        }

        armorStands.get(10).setCustomName(title);


        String editor = switch (type) {
            case 0 -> AQUA.toString() + BOLD + "All Time" + GRAY + " Weekly Daily";
            case 1 -> GRAY + "All Time " + AQUA + BOLD + "Weekly " + GRAY + "Daily";
            default -> GRAY + "All Time Weekly " + AQUA + BOLD + "Daily";
        };

        armorStands.get(11).setCustomName(editor);

        if (!this.hasTimelyPositions()) {
            armorStands.get(11).setCustomNameVisible(false);
        }
    }

    public List<ArmorStand> getNewArmorStands() {
        RelayUtils.log("Getting new armor stands");
        double x = this.getX();
        double y = this.getY();
        double z = this.getZ();
        World world = this.getWorld();
        List<ArmorStand> armorStands = new ArrayList<>();

        for (int i = 10; i > 0; i--) {
            ArmorStand armorStand = this.spawnArmorStand();
            this.prepareArmorStand(armorStand);
            Location location = new Location(world, x, y - 1.5F + 0.3F * i, z);
            armorStand.teleport(location);
            armorStands.add(armorStand);
            this.clearArmorStand(armorStand);
        }

        ArmorStand typeName = this.spawnArmorStand(this.getLocation().add(0, -1.5F + 0.3F * 11, 0));
        this.prepareArmorStand(typeName);
        armorStands.add(typeName);

        ArmorStand typeEditor = this.spawnArmorStand(this.getLocation().add(0, -2F, 0));
        this.prepareArmorStand(typeEditor);
        armorStands.add(typeEditor);

        return armorStands;
    }

    public void prepareArmorStand(ArmorStand armorStand) {
        armorStand.setGravity(false);
        armorStand.setInvisible(false);
        armorStand.setInvisible(true);
    }

    public Location getLocation() {
        return new Location(this.getWorld(), this.getX(), this.getY(), this.getZ());
    }

    public void removeArmorStands() {
        for (ArmorStand allArmorStand : this.getAllArmorStands()) {
            allArmorStand.remove();
        }

        this.allTimeArmorStands.clear();
        this.weeklyArmorStands.clear();
        this.dailyArmorStands.clear();

        this.titleArmorStand.remove();
    }

    public void delete() {
        this.removeArmorStands();

        LEADERBOARDS.remove(this);
    }

    public void info(HubPlayer hubPlayer) {
        hubPlayer.sendInfoMessage("%s -> %d | %d | %d", this.getStatisticName(), (int) this.getX(), (int) this.getY(), (int) this.getZ());
    }

    public void setPosition(Location location) {
        this.x.setValue(location.getX());
        this.y.setValue(location.getY());
        this.z.setValue(location.getZ());
    }

    public void updateData() {
        GameAction gameAction = this.getGameAction();
        String statisticName = this.getStatisticName();
        if (this.getSpecificGameAction() != null) {
            gameAction = this.getSpecificGameAction().getGameAction();
        }

/*
        if ((this.getSpecificGameAction() == null || this.getSpecificGameAction().getGameType() == null) && Main.isGameLobby() && gameAction != null) {
            statisticName = gameAction.getLeaderboardName(Main.mainGame);
        }

        if (Main.isGameLobby() && RelayUtils.EXTRA_COLUMNS.contains(statisticName)) {
            statisticName = Main.getMainGame().specifyName(statisticName);
        }
*/

        if ("tokens".equalsIgnoreCase(statisticName)) {
            statisticName = "credits";
        }

        List<ResultSet> resultSets = Main.getLeaderboards(statisticName);

        if (statisticName.contains("parkour")) {
            statisticName = "time";
        } else if (statisticName.contains("arena")) {
            statisticName = statisticName.split("_")[1];
        } else if (statisticName.equalsIgnoreCase("time_played")) {
            statisticName = "timePlayed";
        }

        for (int i = 0; i < resultSets.size(); i++) {
            Map<UUID, Number> map = new LinkedHashMap<>();
            ResultSet resultSet = resultSets.get(i);

            try {
                while (resultSet.next()) {
                    try {
                        UUID uuid = UUID.fromString(resultSet.getString(ProfileColumn.ID.toString()));
                        Number value;

                        if (gameAction != null && gameAction.isCalculated()) {
                            value = resultSet.getDouble(statisticName);
                        } else {
                            value = resultSet.getLong(statisticName);
                        }

                        map.put(uuid, value);
                    } catch (Exception exception) {
                        Main.error("An error occurred while processing result set in leaderboard (%s): %s", statisticName, exception.getMessage());
                    }
                }

            } catch (Exception exception) {
                Main.error("An error occurred on reading next result set in leaderboard (%s): %s", statisticName, exception.getMessage());
            }

            this.setMap(map, i);
            /*Main.info("%s (%d)", gameAction.getCodeName(), i);
            Main.info("Keys: %s", map.keySet());
            Main.info("Values: %s", map.values());*/
        }

        /*this.allTimeLeaderboard = LeaderboardProfile.getMap(gameActoin, 0);
        this.weeklyLeaderboard = LeaderboardProfile.getMap(gameActoin, 1);
        this.dailyLeaderboard = LeaderboardProfile.getMap(gameActoin, 2);*/

        Main.synchronous(() -> {
            this.updateSpecificLeaderboard(this.allTimeArmorStands, this.allTimeLeaderboard);
            if (this.hasTimelyPositions()) {
                this.updateSpecificLeaderboard(this.weeklyArmorStands, this.weeklyLeaderboard);
                this.updateSpecificLeaderboard(this.dailyArmorStands, this.dailyLeaderboard);
            }

            for (HubPlayer onlineHubPlayer : HubPlayer.getOnlineHubPlayers()) {
                this.updatePersonalArmorStands(onlineHubPlayer);
            }

            List<UUID> toRemove = new ArrayList<>();

            for (Map<UUID, ArmorStand> allPersonalMap : this.getAllPersonalMaps()) {
                for (Map.Entry<UUID, ArmorStand> uuidArmorStandEntry : allPersonalMap.entrySet()) {
                    HubPlayer hubPlayer = HubPlayer.getHubPlayer(uuidArmorStandEntry.getKey());
                    if (hubPlayer == null || !hubPlayer.isOnline()) {
                        uuidArmorStandEntry.getValue().remove();
                        toRemove.add(uuidArmorStandEntry.getKey());
                    }
                }
            }

            for (UUID uuid : toRemove) {
                for (Map<UUID, ArmorStand> allPersonalMap : this.getAllPersonalMaps()) {
                    allPersonalMap.remove(uuid);
                }
            }
        });
    }

    public boolean isParkour() {
        return this.getStatisticName().contains("parkour");
    }

    public boolean isArena() {
        return this.getStatisticName().contains("arena");
    }

    public void setMap(Map<UUID, Number> map, int type) {
        switch (type) {
            case 0 -> this.allTimeLeaderboard = map;
            case 1 -> this.weeklyLeaderboard = map;
        }

        this.dailyLeaderboard = map;
    }

    private static <K, V> Map<K, V> sortByValue(Map<K, V> unsortMap) {
        List<Map.Entry<K, V>> list = new LinkedList<>(unsortMap.entrySet());
        Collections.sort(list, new Comparator<Object>() {
            @SuppressWarnings("unchecked")
            public int compare(Object o1, Object o2) {

                boolean o1Null = o1 == null || ((Map.Entry<K, V>) o1).getValue() == null;
                boolean o2Null = o2 == null || ((Map.Entry<K, V>) o2).getValue() == null;

                if (o1Null && o2Null) return 0;

                if (o1Null) return -1;

                if (o2Null) return 1;

                V a = ((Map.Entry<K, V>) o1).getValue();

                V b = ((Map.Entry<K, V>) o2).getValue();

                return ((Comparable<V>) b).compareTo(a);
            }
        });

        Map<K, V> result = new LinkedHashMap<>();
        for (Map.Entry<K, V> entry : list) {
            result.put(entry.getKey(), entry.getValue());
        }

        return result;
    }

    List<Map<UUID, ArmorStand>> getAllPersonalMaps() {
        List<Map<UUID, ArmorStand>> allPersonalMaps = new ArrayList<>();

        allPersonalMaps.add(this.allTimePersonal);
        allPersonalMaps.add(this.weeklyPersonal);
        allPersonalMaps.add(this.dailyPersonal);

        return allPersonalMaps;
    }

    /*public void processResultSet(Map<UUID, Long> leaderboard, ResultSet resultSet) {
        leaderboard.clear();
        try {
            while (resultSet.next()) {
                UUID uuid = UUID.fromString(resultSet.getString(ProfileColumn.ID.getName()));
                long value = resultSet.getLong(tgi);
                leaderboard.put(uuid, value);
            }
        } catch (Exception exception) {
            Main.error("An error occurred while attempting to process result set: (%s) %s", this.getGameAction().codeName, exception.getMessage());
        }
    }*/

    public void clearArmorStands(List<ArmorStand> armorStands) {
        for (int i = 0; i < 10; i++) {
            this.clearArmorStand(armorStands.get(i));
        }
    }

    public void clearArmorStand(ArmorStand armorStand) {
        armorStand.setCustomName(ChatColor.GRAY + ChatColor.BOLD.toString() + "...");
    }

    public void updateSpecificLeaderboard(List<ArmorStand> armorStands, Map<UUID, Number> leaderboard) {
        try {
            //this.clearArmorStands(armorStands);
            List<UUID> asList = Miscellaneous.getList(leaderboard.keySet());
            int index = 0;
            while (index < 10) {
                ArmorStand armorStand = armorStands.get(index);
                if (index > leaderboard.size() - 1) {
                    this.clearArmorStand(armorStand);
                } else {
                    UUID uuid = asList.get(index);
                    Number value = leaderboard.get(uuid);
                    SmartPlayer smartPlayer = SmartPlayer.getOrCreate(uuid);
                    int finalIndex = index;
                    Main.scheduleSyncDelayedTask(() -> {
                        int number = finalIndex + 1;
                        ComponentBuilder componentBuilder = new ComponentBuilder("%s%s. ",
                                ChatColor.GRAY,
                                number);
                        componentBuilder.append(smartPlayer.getComponentRealFormattedName()).color(GRAY).append(" - ");
                        componentBuilder.appendComponent(this.getDisplayValue(value, armorStands == this.allTimeArmorStands, false).getResult());
                        ((CraftEntity) armorStand).getHandle().b(RelayMCUtils.fromBaseComponent(componentBuilder.getResult()));
                    }, 0.5);
                }
                index++;
            }
        } catch (Exception exception) {
            Main.error("An error occurred while attempting to update specific leaderboard: %s | %s", this.getStatisticName(), exception.getMessage());
        }
    }

    public ComponentBuilder getDisplayValue(Number value, boolean isOverall, boolean personal) {
        if (value.doubleValue() < 0) {
            return new ComponentBuilder().append(GRAY).append("...");
        }

        String statisticName = this.getStatisticName();
        ComponentBuilder componentBuilder = new ComponentBuilder().append(personal ? GOLD : AQUA);

        if (this.isParkour()) {
            return componentBuilder.append(Text.formatTimeFromMilliseconds(value.longValue(), 3, true));
        }

        if (statisticName.contains("time_played")) {
            return componentBuilder.append(Text.getShortTextualFormattedTime(value.longValue()));
        } else {
            if (isOverall) {
                if (statisticName.contains("experience")) {
                    long xp = value.longValue();
                    MainGame mainGame = Main.getMainGame();
                    int level = RelayUtils.calculateLevel(mainGame, xp);
                    return componentBuilder.append(RelayUtils.getColorForLevel(mainGame, level) + String.valueOf(level) + RelayUtils.getLevelCharacter(mainGame, level));
                } else if (statisticName.contains("elo")) {
                    return componentBuilder.appendComponent(SmartPlayer.getEloComponent(value.longValue(), false).getResult());
                }
            }
        }
        boolean ratio = value instanceof Double;

        return componentBuilder.append(ratio ? Main.shortenNumber(value) : Text.addCommasToNumber(value.longValue()));
    }

    public GameAction getGameAction() {
        return GameAction.getGameAction(this.getStatisticName());
    }

    public SpecificGameAction getSpecificGameAction() {
        return SpecificGameAction.getSpecificGameAction(this.getStatisticName());
    }

    public String getStatisticName() {
        String statisticName = this.statisticName.getValue();

        if (Main.isMainLobby()) {
            if (statisticName.equalsIgnoreCase("elo")) {
                statisticName = "tokens";
            }
        }

        return statisticName;
    }

    public void playerJoin(HubPlayer hubPlayer) {
        this.updatePersonalArmorStands(hubPlayer);
        //Main.scheduleSyncDelayedTask(() -> this.showLeaderboard(hubPlayer), 0.5);
    }

    public void playerQuit(HubPlayer hubPlayer) {
        this.removePersonalArmorStands(hubPlayer);
    }

    private void removePersonalArmorStands(HubPlayer hubPlayer) {
        for (int i = 0; i < 3; i++) {
            Map<UUID, ArmorStand> map = this.getPersonalMap(i);

            if (map.containsKey(hubPlayer.getUUID())) {
                ArmorStand armorStand = map.get(hubPlayer.getUUID());
                armorStand.remove();
                map.remove(hubPlayer.getUUID());
            }
        }
    }

    public void playerMove(HubPlayer hubPlayer, Location from, Location to) {
        BoundingBox boundingBox = BoundingBox.of(this.getLocation(), 30D, 30D, 30D);
        if (!boundingBox.contains(from.toVector()) && boundingBox.contains(to.toVector())) {
            this.showLeaderboard(hubPlayer);
        }
    }

    public void updatePersonalArmorStands(HubPlayer hubPlayer) {
        for (int i = 0; i < 3; i++) {
            this.updatePersonalArmorStand(hubPlayer, i);
        }
    }

    public ArmorStand updatePersonalArmorStand(HubPlayer hubPlayer, int type) {
        Map<UUID, ArmorStand> map = this.getPersonalMap(type);

        ArmorStand armorStand;
        if (!map.containsKey(hubPlayer.getUUID())) {
            RelayUtils.log("Creating armor stand for %s", hubPlayer.getName());
            armorStand = this.spawnArmorStand(this.getLocation().add(0, -1.6F, 0));
            this.prepareArmorStand(armorStand);
            for (HubPlayer onlineHubPlayer : HubPlayer.getOnlineHubPlayers()) {
                if (onlineHubPlayer != hubPlayer) {
                    onlineHubPlayer.hideEntity(armorStand);
                }
            }
            map.put(hubPlayer.getUUID(), armorStand);
        } else {
            armorStand = map.get(hubPlayer.getUUID());
            armorStand.setInvisible(true);
        }

        Map.Entry<Integer, Number> positionAndValue = this.getPositionOfPlayer(hubPlayer, type);
        int position = positionAndValue.getKey();
        Number value = positionAndValue.getValue();
        ComponentBuilder componentBuilder = new ComponentBuilder("%s%s%s. ",
                BOLD,
                GRAY,
                position == -1 ? "?" : position + 1);
        Group group = hubPlayer.getHighestGroup();
        componentBuilder.color(group == Group.CHAMPION ? RelayUtils.getChampionTierColor(hubPlayer.getSmartPlayer().getChampionTier()) : hubPlayer.getHighestGroup().getOther())
                .append(hubPlayer.getRealName()).color(GRAY).append(" - ");
        componentBuilder.appendComponent(this.getDisplayValue(value, type == 0, true).getResult());
        ((CraftEntity) armorStand).getHandle().b(RelayMCUtils.fromBaseComponent(componentBuilder.getResult()));

        return armorStand;
    }

    public Map.Entry<Integer, Number> getPositionOfPlayer(HubPlayer hubPlayer, int type) {
        Map<UUID, Number> leaderboard = this.getLeaderboardOfType(type);
        int position = Miscellaneous.getList(leaderboard.keySet()).indexOf(hubPlayer.getUUID());
        Number value = leaderboard.getOrDefault(hubPlayer.getUUID(), hubPlayer.getStatistic(type, this.getStatisticName()));
        return new AbstractMap.SimpleEntry<>(position, value);
    }

    public Map<UUID, Number> getLeaderboardOfType(int type) {
        return switch (type) {
            case 0 -> this.allTimeLeaderboard;
            case 1 -> this.weeklyLeaderboard;
            default -> this.dailyLeaderboard;
        };

    }

    public ArmorStand getPersonalArmorStand(HubPlayer hubPlayer, int type) {
        return this.updatePersonalArmorStand(hubPlayer, type);
    }

    public World getWorld() {
        return Bukkit.getWorld("overworld");
    }

    public Map<UUID, ArmorStand> getPersonalMap(int type) {
        return switch (type) {
            case 0 -> this.allTimePersonal;
            case 1 -> this.weeklyPersonal;
            default -> this.dailyPersonal;
        };
    }

    public void showLeaderboard(HubPlayer hubPlayer) {
        this.showLeaderboard(hubPlayer, -1);
    }

    public List<ArmorStand> getArmorStandsOfType(int type) {
        return switch (type) {
            case 0 -> this.allTimeArmorStands;
            case 1 -> this.weeklyArmorStands;
            default -> this.dailyArmorStands;
        };
    }

    public void hidePersonal(HubPlayer hubPlayer) {
        for (Map<UUID, ArmorStand> hubPlayerArmorStandMap : Miscellaneous.getList(this.allTimePersonal, this.weeklyPersonal, this.dailyPersonal)) {
            hubPlayerArmorStandMap.forEach((hubPlayer1, armorStand) -> {
                if (!hubPlayer1.equals(hubPlayer.getUUID())) {
                    hubPlayer.hideEntity(armorStand);
                }
            });
        }
    }

    public void showLeaderboard(HubPlayer hubPlayer, int previous) {
        for (StatisticsLeaderboard leaderboard : StatisticsLeaderboard.LEADERBOARDS) {
            Collection<ArmorStand> armorStands = getWorld().getNearbyEntitiesByType(ArmorStand.class, leaderboard.getLocation(), 2);
            for (ArmorStand armorStand : armorStands) {
                if (getFromStand(armorStand) == null && Miscellaneous.match(Parkour.PARKOUR_LIST, parkour -> Miscellaneous.match(parkour.getArmorStands(), armorStand1 -> armorStand1 == armorStand) != null) == null) {
                    armorStand.remove();
                }
            }
        }
        if (previous == -1) {
            for (ArmorStand armorStand : this.getAllArmorStands()) {
                hubPlayer.setEntityNameVisible(armorStand, false);
            }
        } else {
            ArmorStand armorStand = this.getPersonalMap(previous).get(hubPlayer.getUUID());
            if (armorStand != null) {
                hubPlayer.setEntityNameVisible(armorStand, false);
            }
            for (ArmorStand stand : this.getArmorStandsOfType(previous)) {
                hubPlayer.setEntityNameVisible(stand, false);
            }
        }

        this.hidePersonal(hubPlayer);

        int type = this.getTypeForPlayer(hubPlayer);
        ArmorStand personalOfType = this.getPersonalArmorStand(hubPlayer, type);
        hubPlayer.setEntityNameVisible(personalOfType, true);

        switch (type) {
            case 0 -> {
                for (int i = 0; i < this.allTimeArmorStands.size(); i++) {
                    ArmorStand armorStand = this.allTimeArmorStands.get(i);
                    if (this.hasTimelyPositions() || i != 11) {

                        hubPlayer.setEntityNameVisible(armorStand, true);
                    }
                }
            }
            case 1 -> {
                for (ArmorStand weeklyArmorStand : this.weeklyArmorStands) {
                    hubPlayer.setEntityNameVisible(weeklyArmorStand, true);
                }
            }
            default -> {
                for (ArmorStand dailyArmorStand : this.dailyArmorStands) {
                    hubPlayer.setEntityNameVisible(dailyArmorStand, true);
                }
            }
        }

        hubPlayer.setEntityNameVisible(this.titleArmorStand, true);
    }

    public List<ArmorStand> getAllArmorStands() {
        List<ArmorStand> allArmorStands = new ArrayList<>();

        allArmorStands.addAll(this.allTimeArmorStands);
        allArmorStands.addAll(this.allTimePersonal.values());
        allArmorStands.addAll(this.weeklyArmorStands);
        allArmorStands.addAll(this.weeklyPersonal.values());
        allArmorStands.addAll(this.dailyArmorStands);
        allArmorStands.addAll(this.dailyPersonal.values());
        allArmorStands.add(this.titleArmorStand);

        return allArmorStands;
    }

    public void playerInteractEntity(Player player, int entityId) {
        RelayUtils.log("Checking for leaderboard interact %s", this.getStatisticName());
        if (!this.hasTimelyPositions()) {
            RelayUtils.log("Cancelelind due to no timely positions");
            return;
        }
        for (ArmorStand allArmorStand : this.getAllArmorStands()) {
            if (allArmorStand.getEntityId() == entityId) {
                this.cycleLeaderboard(HubPlayer.getHubPlayer(player));
                break;
            }
        }

        RelayUtils.log("Requested id: %d", entityId);
        RelayUtils.log("This IDs: %s", Text.separateWithCommas(this.getAllArmorStands(), armorStand -> String.valueOf(armorStand.getEntityId())));
    }

    public boolean hasTimelyPositions() {
        return !(this.isParkour() || this.isArena() || this.getStatisticName().contains("winstreak") || (Main.isMainLobby() && Miscellaneous.getList("experience", "tokens", "time_played").contains(this.getStatisticName())));
    }

    public void cycleLeaderboard(HubPlayer hubPlayer) {
        int previous = this.getTypeForPlayer(hubPlayer);
        int current = previous == 2 ? 0 : previous + 1;
        this.playerToLeaderboardType.put(hubPlayer.getUUID(), current);
        this.showLeaderboard(hubPlayer, previous);

        String type = switch (current) {
            case 0 -> "All Time";
            case 1 -> "Weekly";
            default -> "Daily";
        };

        hubPlayer.sendInfoMessage("Now showing %s%s%s positions.", AQUA + BOLD.toString(), type, YELLOW);
        hubPlayer.playClickSound();
    }

    public int getTypeForPlayer(HubPlayer hubPlayer) {
        if (!this.playerToLeaderboardType.containsKey(hubPlayer.getUUID())) {
            this.playerToLeaderboardType.put(hubPlayer.getUUID(), 0);
            return 0;
        }

        return this.playerToLeaderboardType.get(hubPlayer.getUUID());
    }

    public String getSmartName() {
        return this.id + "-" + this.statisticName.getValue();
    }

    public static StatisticsLeaderboard createFromJson(JsonObject jsonObject) {
        StatisticsLeaderboard leaderboard = new StatisticsLeaderboard(0, 0, 0, "experience");

        leaderboard.fromJson(jsonObject);

        return leaderboard;
    }

    public static void onPlayerJoin(HubPlayer hubPlayer) {
        for (StatisticsLeaderboard leaderboard : LEADERBOARDS) {
            leaderboard.playerJoin(hubPlayer);
        }
    }

    public static void onPlayerQuit(HubPlayer hubPlayer) {
        for (StatisticsLeaderboard leaderboard : LEADERBOARDS) {
            leaderboard.playerQuit(hubPlayer);
        }
    }

    public static void showLeaderboards(HubPlayer hubPlayer) {
        for (StatisticsLeaderboard leaderboard : LEADERBOARDS) {
            leaderboard.showLeaderboard(hubPlayer);
        }
    }

    public static void onPlayerMove(Player player, Location from, Location to) {
        for (StatisticsLeaderboard leaderboard : LEADERBOARDS) {
            leaderboard.playerMove(HubPlayer.getHubPlayer(player), from, to);
        }
    }

    public static void updateLeaderboards() {
        Main.async(() -> {
            //LeaderboardProfile.refresh();
            for (StatisticsLeaderboard leaderboard : LEADERBOARDS) {
                leaderboard.updateData();
            }
        });
    }

    public static void onTick() {

    }

    public static void onPlayerInteractEntity(Player player, int entityId) {
        for (StatisticsLeaderboard leaderboard : LEADERBOARDS) {
            leaderboard.playerInteractEntity(player, entityId);
        }
    }

    public static boolean hasLeaderboardSelected(HubPlayer hubPlayer) {
        return getSelectedLeaderboard(hubPlayer) != null;
    }

    public static StatisticsLeaderboard getSelectedLeaderboard(HubPlayer hubPlayer) {
        for (StatisticsLeaderboard leaderboard : LEADERBOARDS) {
            if (leaderboard.id == HUB_PLAYER_TO_SELECTED.getOrDefault(hubPlayer, -1)) {
                return leaderboard;
            }
        }

        return null;
    }

    public static StatisticsLeaderboard getLeaderboard(String argument) {
        for (StatisticsLeaderboard leaderboard : LEADERBOARDS) {
            if (String.valueOf(leaderboard.id).equalsIgnoreCase(argument) || leaderboard.getSmartName().equalsIgnoreCase(argument)) {
                return leaderboard;
            }
        }

        return null;
    }

    public static void selectLeaderboard(HubPlayer hubPlayer, int id) {
        HUB_PLAYER_TO_SELECTED.put(hubPlayer, id);
    }

    public static void deleteLeaderboard(HubPlayer hubPlayer) {
        StatisticsLeaderboard leaderboard = getSelectedLeaderboard(hubPlayer);
        if (leaderboard == null) return;

        leaderboard.delete();
    }

    public static void teleportLeaderboard(HubPlayer hubPlayer) {
        StatisticsLeaderboard leaderboard = getSelectedLeaderboard(hubPlayer);
        if (leaderboard == null) return;

        leaderboard.removeArmorStands();
        leaderboard.setPosition(hubPlayer.getLocation().add(0, 0.4, 0));
        leaderboard.createArmorStands();
    }

    public static void showInfo(HubPlayer hubPlayer) {
        StatisticsLeaderboard leaderboard = getSelectedLeaderboard(hubPlayer);
        if (leaderboard != null)
            leaderboard.info(hubPlayer);
    }

    public static void listLeaderboards(HubPlayer hubPlayer) {
        if (LEADERBOARDS.isEmpty()) {
            hubPlayer.sendInfoMessage("There are no leaderboards to display.");
        } else {
            hubPlayer.sendInfoMessage("-----------");
            for (StatisticsLeaderboard leaderboard : LEADERBOARDS) {
                leaderboard.info(hubPlayer);
            }
            hubPlayer.sendInfoMessage("-----------");
        }
    }

    public static List<String> getAllSmartNames() {
        List<String> names = new ArrayList<>();
        for (StatisticsLeaderboard leaderboard : LEADERBOARDS) {
            names.add(leaderboard.getSmartName());
        }

        return names;
    }

    public static void readConfig(JsonObject jsonObject) {
        if (!jsonObject.has("leaderboards")) {
            Main.error("No leaderboards in config");
            return;
        }

        JsonArray jsonArray = jsonObject.get("leaderboards").getAsJsonArray();
        for (JsonElement jsonElement : jsonArray) {
            try {
                createFromJson(jsonElement.getAsJsonObject());
            } catch (Exception exception) {
                RelayMCUtils.error("An error occurred while attempting to create a leaderboard from the config: %s (%s)", exception.getMessage(), jsonElement.getAsJsonObject());
            }
        }
    }

    public static JsonArray getLeaderboardsAsJsonArray() {
        JsonArray jsonArray = new JsonArray();

        for (StatisticsLeaderboard leaderboard : LEADERBOARDS) {
            jsonArray.add(leaderboard.toJson());
        }

        return jsonArray;
    }

    public static void clear() {
        while (!LEADERBOARDS.isEmpty()) {
            LEADERBOARDS.get(0).delete();
        }
    }
}