// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.server.core.modules.serverplayerlist;

import com.hypixel.hytale.metrics.metric.HistoricMetric;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.protocol.packets.connection.PongType;
import com.hypixel.hytale.server.core.io.PacketHandler;
import java.util.Map;
import com.hypixel.hytale.protocol.packets.interface_.UpdateServerPlayerListPing;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.protocol.packets.interface_.UpdateServerPlayerList;
import com.hypixel.hytale.protocol.packets.interface_.ServerPlayerListUpdate;
import com.hypixel.hytale.protocol.packets.interface_.RemoveFromServerPlayerList;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import com.hypixel.hytale.protocol.Packet;
import com.hypixel.hytale.protocol.packets.interface_.AddToServerPlayerList;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.protocol.packets.interface_.ServerPlayerListPlayer;
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.event.EventRegistry;
import java.util.concurrent.TimeUnit;
import com.hypixel.hytale.server.core.HytaleServer;
import com.hypixel.hytale.server.core.event.events.player.AddPlayerToWorldEvent;
import com.hypixel.hytale.server.core.event.events.player.PlayerDisconnectEvent;
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import javax.annotation.Nonnull;
import com.hypixel.hytale.common.plugin.PluginManifest;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;

public class ServerPlayerListModule extends JavaPlugin
{
    @Nonnull
    public static final PluginManifest MANIFEST;
    private static final int PING_UPDATE_INTERVAL_SECONDS = 10;
    private static ServerPlayerListModule instance;
    
    @Nonnull
    public static ServerPlayerListModule get() {
        return ServerPlayerListModule.instance;
    }
    
    public ServerPlayerListModule(@Nonnull final JavaPluginInit init) {
        super(init);
        ServerPlayerListModule.instance = this;
    }
    
    @Override
    protected void setup() {
        final EventRegistry eventRegistry = this.getEventRegistry();
        eventRegistry.register(PlayerConnectEvent.class, this::onPlayerConnect);
        eventRegistry.register(PlayerDisconnectEvent.class, this::onPlayerDisconnect);
        eventRegistry.registerGlobal(AddPlayerToWorldEvent.class, this::onPlayerAddedToWorld);
        HytaleServer.SCHEDULED_EXECUTOR.scheduleWithFixedDelay(this::broadcastPingUpdates, 10L, 10L, TimeUnit.SECONDS);
    }
    
    private void onPlayerConnect(@Nonnull final PlayerConnectEvent event) {
        final PlayerRef joiningPlayerRef = event.getPlayerRef();
        final UUID joiningPlayerUuid = joiningPlayerRef.getUuid();
        final List<PlayerRef> allPlayers = Universe.get().getPlayers();
        final ServerPlayerListPlayer[] serverListPlayers = new ServerPlayerListPlayer[allPlayers.size()];
        int index = 0;
        for (final PlayerRef playerRef : allPlayers) {
            serverListPlayers[index++] = createServerPlayerListPlayer(playerRef);
        }
        final AddToServerPlayerList fullListPacket = new AddToServerPlayerList(serverListPlayers);
        joiningPlayerRef.getPacketHandler().write(fullListPacket);
        final AddToServerPlayerList newPlayerPacket = new AddToServerPlayerList(new ServerPlayerListPlayer[] { createServerPlayerListPlayer(joiningPlayerRef) });
        for (final PlayerRef playerRef2 : allPlayers) {
            if (!playerRef2.getUuid().equals(joiningPlayerUuid)) {
                playerRef2.getPacketHandler().write(newPlayerPacket);
            }
        }
    }
    
    private void onPlayerDisconnect(@Nonnull final PlayerDisconnectEvent event) {
        final PlayerRef leavingPlayerRef = event.getPlayerRef();
        final UUID leavingPlayerUuid = leavingPlayerRef.getUuid();
        final RemoveFromServerPlayerList removePacket = new RemoveFromServerPlayerList(new UUID[] { leavingPlayerUuid });
        for (final PlayerRef playerRef : Universe.get().getPlayers()) {
            if (!playerRef.getUuid().equals(leavingPlayerUuid)) {
                playerRef.getPacketHandler().write(removePacket);
            }
        }
    }
    
    private void onPlayerAddedToWorld(@Nonnull final AddPlayerToWorldEvent event) {
        final Holder<EntityStore> holder = event.getHolder();
        final PlayerRef playerRefComponent = holder.getComponent(PlayerRef.getComponentType());
        if (playerRefComponent == null) {
            return;
        }
        final UUID playerUuid = playerRefComponent.getUuid();
        final UUID worldUuid = event.getWorld().getWorldConfig().getUuid();
        final UpdateServerPlayerList updatePacket = new UpdateServerPlayerList(new ServerPlayerListUpdate[] { new ServerPlayerListUpdate(playerUuid, worldUuid) });
        for (final PlayerRef otherPlayerRef : Universe.get().getPlayers()) {
            otherPlayerRef.getPacketHandler().write(updatePacket);
        }
    }
    
    private void broadcastPingUpdates() {
        final List<PlayerRef> allPlayers = Universe.get().getPlayers();
        if (allPlayers.isEmpty()) {
            return;
        }
        final Object2IntOpenHashMap<UUID> pingMap = new Object2IntOpenHashMap<UUID>(allPlayers.size());
        for (final PlayerRef playerRef : allPlayers) {
            pingMap.put(playerRef.getUuid(), getPingValue(playerRef.getPacketHandler()));
        }
        final UpdateServerPlayerListPing packet = new UpdateServerPlayerListPing(pingMap);
        for (final PlayerRef playerRef2 : allPlayers) {
            playerRef2.getPacketHandler().writeNoCache(packet);
        }
    }
    
    private static int getPingValue(@Nonnull final PacketHandler handler) {
        final HistoricMetric historicMetric = handler.getPingInfo(PongType.Direct).getPingMetricSet();
        final double average = historicMetric.getAverage(0);
        return (int)PacketHandler.PingInfo.TIME_UNIT.toMillis(MathUtil.fastCeil(average));
    }
    
    @Nonnull
    private static ServerPlayerListPlayer createServerPlayerListPlayer(@Nonnull final PlayerRef playerRef) {
        return new ServerPlayerListPlayer(playerRef.getUuid(), playerRef.getUsername(), playerRef.getWorldUuid(), getPingValue(playerRef.getPacketHandler()));
    }
    
    static {
        MANIFEST = PluginManifest.corePlugin(ServerPlayerListModule.class).depends(Universe.class).build();
    }
}
