package net.minecraft.client.multiplayer;

import org.apache.logging.log4j.LogManager;
import java.util.Iterator;
import io.netty.channel.socket.nio.NioSocketChannel;
import net.minecraft.util.Mth;
import com.google.common.collect.Iterables;
import java.nio.charset.StandardCharsets;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.channel.ChannelFutureListener;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.buffer.ByteBuf;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelException;
import io.netty.channel.ChannelOption;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.bootstrap.Bootstrap;
import java.net.UnknownHostException;
import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket;
import net.minecraft.network.protocol.handshake.ClientIntentionPacket;
import net.minecraft.network.ConnectionProtocol;
import net.minecraft.network.PacketListener;
import net.minecraft.network.protocol.status.ClientboundPongResponsePacket;
import com.mojang.authlib.GameProfile;
import net.minecraft.network.protocol.status.ServerStatus;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.status.ServerboundPingRequestPacket;
import net.minecraft.Util;
import org.apache.commons.lang3.ArrayUtils;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket;
import net.minecraft.network.protocol.status.ClientStatusPacketListener;
import net.minecraft.client.resources.language.I18n;
import java.net.InetAddress;
import java.util.Collections;
import com.google.common.collect.Lists;
import net.minecraft.network.Connection;
import java.util.List;
import org.apache.logging.log4j.Logger;
import com.google.common.base.Splitter;

public class ServerStatusPinger {
    private static final Splitter SPLITTER;
    private static final Logger LOGGER;
    private final List<Connection> connections;
    
    public ServerStatusPinger() {
        this.connections = Collections.<Connection>synchronizedList(Lists.newArrayList());
    }
    
    public void pingServer(final ServerData dnu) throws UnknownHostException {
        final ServerAddress dnt3 = ServerAddress.parseString(dnu.ip);
        final Connection kt4 = Connection.connectToServer(InetAddress.getByName(dnt3.getHost()), dnt3.getPort(), false);
        this.connections.add(kt4);
        dnu.motd = I18n.get("multiplayer.status.pinging");
        dnu.ping = -1L;
        dnu.playerList = null;
        kt4.setListener(new ClientStatusPacketListener() {
            private boolean success;
            private boolean receivedPing;
            private long pingStart;
            
            @Override
            public void handleStatusResponse(final ClientboundStatusResponsePacket rv) {
                if (this.receivedPing) {
                    kt4.disconnect(new TranslatableComponent("multiplayer.status.unrequested", new Object[0]));
                    return;
                }
                this.receivedPing = true;
                final ServerStatus rw3 = rv.getStatus();
                if (rw3.getDescription() != null) {
                    dnu.motd = rw3.getDescription().getColoredString();
                }
                else {
                    dnu.motd = "";
                }
                if (rw3.getVersion() != null) {
                    dnu.version = rw3.getVersion().getName();
                    dnu.protocol = rw3.getVersion().getProtocol();
                }
                else {
                    dnu.version = I18n.get("multiplayer.status.old");
                    dnu.protocol = 0;
                }
                if (rw3.getPlayers() != null) {
                    dnu.status = ChatFormatting.GRAY + "" + rw3.getPlayers().getNumPlayers() + "" + ChatFormatting.DARK_GRAY + "/" + ChatFormatting.GRAY + rw3.getPlayers().getMaxPlayers();
                    if (ArrayUtils.isNotEmpty((Object[])rw3.getPlayers().getSample())) {
                        final StringBuilder stringBuilder4 = new StringBuilder();
                        for (final GameProfile gameProfile8 : rw3.getPlayers().getSample()) {
                            if (stringBuilder4.length() > 0) {
                                stringBuilder4.append("\n");
                            }
                            stringBuilder4.append(gameProfile8.getName());
                        }
                        if (rw3.getPlayers().getSample().length < rw3.getPlayers().getNumPlayers()) {
                            if (stringBuilder4.length() > 0) {
                                stringBuilder4.append("\n");
                            }
                            stringBuilder4.append(I18n.get("multiplayer.status.and_more", rw3.getPlayers().getNumPlayers() - rw3.getPlayers().getSample().length));
                        }
                        dnu.playerList = stringBuilder4.toString();
                    }
                }
                else {
                    dnu.status = ChatFormatting.DARK_GRAY + I18n.get("multiplayer.status.unknown");
                }
                if (rw3.getFavicon() != null) {
                    final String string4 = rw3.getFavicon();
                    if (string4.startsWith("data:image/png;base64,")) {
                        dnu.setIconB64(string4.substring("data:image/png;base64,".length()));
                    }
                    else {
                        ServerStatusPinger.LOGGER.error("Invalid server icon (unknown format)");
                    }
                }
                else {
                    dnu.setIconB64(null);
                }
                this.pingStart = Util.getMillis();
                kt4.send(new ServerboundPingRequestPacket(this.pingStart));
                this.success = true;
            }
            
            @Override
            public void handlePongResponse(final ClientboundPongResponsePacket ru) {
                final long long3 = this.pingStart;
                final long long4 = Util.getMillis();
                dnu.ping = long4 - long3;
                kt4.disconnect(new TranslatableComponent("multiplayer.status.finished", new Object[0]));
            }
            
            @Override
            public void onDisconnect(final Component lf) {
                if (!this.success) {
                    ServerStatusPinger.LOGGER.error("Can't ping {}: {}", dnu.ip, lf.getString());
                    dnu.motd = ChatFormatting.DARK_RED + I18n.get("multiplayer.status.cannot_connect");
                    dnu.status = "";
                    ServerStatusPinger.this.pingLegacyServer(dnu);
                }
            }
            
            @Override
            public Connection getConnection() {
                return kt4;
            }
        });
        try {
            kt4.send(new ClientIntentionPacket(dnt3.getHost(), dnt3.getPort(), ConnectionProtocol.STATUS));
            kt4.send(new ServerboundStatusRequestPacket());
        }
        catch (Throwable throwable5) {
            ServerStatusPinger.LOGGER.error(throwable5);
        }
    }
    
    private void pingLegacyServer(final ServerData dnu) {
        final ServerAddress dnt3 = ServerAddress.parseString(dnu.ip);
        ((Bootstrap)((Bootstrap)((Bootstrap)new Bootstrap().group((EventLoopGroup)Connection.NETWORK_WORKER_GROUP.get())).handler((ChannelHandler)new ChannelInitializer<Channel>() {
            protected void initChannel(final Channel channel) throws Exception {
                try {
                    channel.config().setOption(ChannelOption.TCP_NODELAY, true);
                }
                catch (ChannelException ex) {}
                channel.pipeline().addLast(new ChannelHandler[] { (ChannelHandler)new SimpleChannelInboundHandler<ByteBuf>() {
                        public void channelActive(final ChannelHandlerContext channelHandlerContext) throws Exception {
                            super.channelActive(channelHandlerContext);
                            final ByteBuf byteBuf3 = Unpooled.buffer();
                            try {
                                byteBuf3.writeByte(254);
                                byteBuf3.writeByte(1);
                                byteBuf3.writeByte(250);
                                char[] arr4 = "MC|PingHost".toCharArray();
                                byteBuf3.writeShort(arr4.length);
                                for (final char character8 : arr4) {
                                    byteBuf3.writeChar((int)character8);
                                }
                                byteBuf3.writeShort(7 + 2 * dnt3.getHost().length());
                                byteBuf3.writeByte(127);
                                arr4 = dnt3.getHost().toCharArray();
                                byteBuf3.writeShort(arr4.length);
                                for (final char character8 : arr4) {
                                    byteBuf3.writeChar((int)character8);
                                }
                                byteBuf3.writeInt(dnt3.getPort());
                                channelHandlerContext.channel().writeAndFlush(byteBuf3).addListener((GenericFutureListener)ChannelFutureListener.CLOSE_ON_FAILURE);
                            }
                            finally {
                                byteBuf3.release();
                            }
                        }
                        
                        protected void channelRead0(final ChannelHandlerContext channelHandlerContext, final ByteBuf byteBuf) throws Exception {
                            final short short4 = byteBuf.readUnsignedByte();
                            if (short4 == 255) {
                                final String string5 = new String(byteBuf.readBytes(byteBuf.readShort() * 2).array(), StandardCharsets.UTF_16BE);
                                final String[] arr6 = Iterables.<String>toArray(ServerStatusPinger.SPLITTER.split(string5), String.class);
                                if ("§1".equals(arr6[0])) {
                                    final int integer7 = Mth.getInt(arr6[1], 0);
                                    final String string6 = arr6[2];
                                    final String string7 = arr6[3];
                                    final int integer8 = Mth.getInt(arr6[4], -1);
                                    final int integer9 = Mth.getInt(arr6[5], -1);
                                    dnu.protocol = -1;
                                    dnu.version = string6;
                                    dnu.motd = string7;
                                    dnu.status = ChatFormatting.GRAY + "" + integer8 + "" + ChatFormatting.DARK_GRAY + "/" + ChatFormatting.GRAY + integer9;
                                }
                            }
                            channelHandlerContext.close();
                        }
                        
                        public void exceptionCaught(final ChannelHandlerContext channelHandlerContext, final Throwable throwable) throws Exception {
                            channelHandlerContext.close();
                        }
                    } });
            }
        })).channel((Class)NioSocketChannel.class)).connect(dnt3.getHost(), dnt3.getPort());
    }
    
    public void tick() {
        synchronized (this.connections) {
            final Iterator<Connection> iterator3 = this.connections.iterator();
            while (iterator3.hasNext()) {
                final Connection kt4 = iterator3.next();
                if (kt4.isConnected()) {
                    kt4.tick();
                }
                else {
                    iterator3.remove();
                    kt4.handleDisconnection();
                }
            }
        }
    }
    
    public void removeAll() {
        synchronized (this.connections) {
            final Iterator<Connection> iterator3 = this.connections.iterator();
            while (iterator3.hasNext()) {
                final Connection kt4 = iterator3.next();
                if (kt4.isConnected()) {
                    iterator3.remove();
                    kt4.disconnect(new TranslatableComponent("multiplayer.status.cancelled", new Object[0]));
                }
            }
        }
    }
    
    static {
        SPLITTER = Splitter.on('\0').limit(6);
        LOGGER = LogManager.getLogger();
    }
}
