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

package com.hypixel.hytale.common.util;

import java.util.function.Predicate;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.util.Locale;
import java.net.UnknownHostException;
import java.net.InetSocketAddress;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.net.SocketException;
import java.util.Enumeration;
import java.net.NetworkInterface;
import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.Inet6Address;

public class NetworkUtil
{
    public static Inet6Address ANY_IPV6_ADDRESS;
    public static Inet4Address ANY_IPV4_ADDRESS;
    public static Inet6Address LOOPBACK_IPV6_ADDRESS;
    public static Inet4Address LOOPBACK_IPV4_ADDRESS;
    
    @Nullable
    public static InetAddress getFirstNonLoopbackAddress() throws SocketException {
        InetAddress firstInet6Address = null;
        final Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
        while (networkInterfaces.hasMoreElements()) {
            final NetworkInterface networkInterface = networkInterfaces.nextElement();
            if (!networkInterface.isLoopback()) {
                if (!networkInterface.isUp()) {
                    continue;
                }
                final Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
                while (inetAddresses.hasMoreElements()) {
                    final InetAddress inetAddress = inetAddresses.nextElement();
                    if (!inetAddress.isLoopbackAddress() && !inetAddress.isAnyLocalAddress()) {
                        if (inetAddress.isLinkLocalAddress()) {
                            continue;
                        }
                        if (inetAddress instanceof Inet4Address) {
                            return inetAddress;
                        }
                        if (!(inetAddress instanceof Inet6Address) || firstInet6Address != null) {
                            continue;
                        }
                        firstInet6Address = inetAddress;
                    }
                }
            }
        }
        return firstInet6Address;
    }
    
    @Nullable
    public static InetAddress getFirstAddressWith(final AddressType... include) throws SocketException {
        return getFirstAddressWith(include, null);
    }
    
    @Nullable
    public static InetAddress getFirstAddressWithout(final AddressType... include) throws SocketException {
        return getFirstAddressWith(null, include);
    }
    
    @Nullable
    public static InetAddress getFirstAddressWith(@Nullable final AddressType[] include, @Nullable final AddressType[] exclude) throws SocketException {
        InetAddress firstInet6Address = null;
        final Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
        while (networkInterfaces.hasMoreElements()) {
            final NetworkInterface networkInterface = networkInterfaces.nextElement();
            if (!networkInterface.isLoopback()) {
                if (!networkInterface.isUp()) {
                    continue;
                }
                final Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
            Label_0052:
                while (inetAddresses.hasMoreElements()) {
                    final InetAddress inetAddress = inetAddresses.nextElement();
                    if (include != null) {
                        for (final AddressType addressType : include) {
                            if (!addressType.predicate.test(inetAddress)) {
                                continue Label_0052;
                            }
                        }
                    }
                    if (exclude != null) {
                        for (final AddressType addressType : exclude) {
                            if (addressType.predicate.test(inetAddress)) {
                                continue Label_0052;
                            }
                        }
                    }
                    if (inetAddress instanceof Inet4Address) {
                        return inetAddress;
                    }
                    if (!(inetAddress instanceof Inet6Address) || firstInet6Address != null) {
                        continue;
                    }
                    firstInet6Address = inetAddress;
                }
            }
        }
        return firstInet6Address;
    }
    
    public static boolean addressMatchesAll(final InetAddress address, @Nonnull final AddressType... types) {
        for (final AddressType type : types) {
            if (!type.predicate.test(address)) {
                return false;
            }
        }
        return true;
    }
    
    public static boolean addressMatchesAny(final InetAddress address) {
        return addressMatchesAny(address, AddressType.values());
    }
    
    public static boolean addressMatchesAny(final InetAddress address, @Nonnull final AddressType... types) {
        for (final AddressType type : types) {
            if (type.predicate.test(address)) {
                return true;
            }
        }
        return false;
    }
    
    @Nonnull
    public static String toSocketString(@Nonnull final InetSocketAddress address) {
        String str;
        if (address.getAddress() instanceof Inet6Address) {
            final String host = address.getHostString();
            if (host.indexOf(58) >= 0) {
                str = "[" + host;
            }
            else {
                str = host;
            }
            str = str + ":" + address.getPort();
        }
        else {
            str = address.getHostString() + ":" + address.getPort();
        }
        return str;
    }
    
    @Nullable
    public static String getHostName() {
        String localhost = null;
        try {
            final InetAddress localHost = InetAddress.getLocalHost();
            localhost = localHost.getHostName();
            if (isAcceptableHostName(localhost)) {
                return localhost;
            }
            final String hostName = localHost.getCanonicalHostName();
            if (isAcceptableHostName(hostName)) {
                return hostName;
            }
        }
        catch (final UnknownHostException ex) {}
        String hostName2 = System.getenv("HOSTNAME");
        if (isAcceptableHostName(hostName2)) {
            return hostName2;
        }
        hostName2 = System.getenv("COMPUTERNAME");
        if (isAcceptableHostName(hostName2)) {
            return hostName2;
        }
        hostName2 = firstLineIfExists("/etc/hostname");
        if (isAcceptableHostName(hostName2)) {
            return hostName2;
        }
        hostName2 = firstLineIfExists("/proc/sys/kernel/hostname");
        if (isAcceptableHostName(hostName2)) {
            return hostName2;
        }
        try {
            final Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();
            while (en.hasMoreElements()) {
                final NetworkInterface ni = en.nextElement();
                if (ni.isUp() && !ni.isLoopback()) {
                    if (ni.isPointToPoint()) {
                        continue;
                    }
                    final String name = ni.getName().toLowerCase(Locale.ROOT);
                    if (name.startsWith("lo") || name.startsWith("docker") || name.startsWith("br-") || name.startsWith("veth") || name.startsWith("virbr") || name.startsWith("utun") || name.startsWith("wg")) {
                        continue;
                    }
                    if (name.startsWith("zt")) {
                        continue;
                    }
                    final Enumeration<InetAddress> e = ni.getInetAddresses();
                    while (e.hasMoreElements()) {
                        final InetAddress a = e.nextElement();
                        if (!a.isLoopbackAddress() && !a.isLinkLocalAddress()) {
                            if (a.isAnyLocalAddress()) {
                                continue;
                            }
                            final String hostAddress = a.getHostAddress();
                            final String addressHostName = a.getHostName();
                            if (addressHostName != null && !addressHostName.equals(hostAddress) && isAcceptableHostName(addressHostName)) {
                                return addressHostName;
                            }
                            final String canonicalHostName = a.getCanonicalHostName();
                            if (canonicalHostName != null && !canonicalHostName.equals(hostAddress) && isAcceptableHostName(canonicalHostName)) {
                                return canonicalHostName;
                            }
                            continue;
                        }
                    }
                }
            }
        }
        catch (final SocketException ex2) {}
        return null;
    }
    
    @Nullable
    private static String firstLineIfExists(final String path) {
        try {
            final Path p = Path.of(path, new String[0]);
            if (!Files.isRegularFile(p, new LinkOption[0])) {
                return null;
            }
            try (final BufferedReader reader = Files.newBufferedReader(p, StandardCharsets.UTF_8)) {
                final String line = reader.readLine();
                return (line == null) ? null : line.trim();
            }
        }
        catch (final IOException e) {
            return null;
        }
    }
    
    private static boolean isAcceptableHostName(@Nullable String name) {
        if (name == null) {
            return false;
        }
        name = name.trim();
        if (name.isEmpty()) {
            return false;
        }
        final String lower = name.toLowerCase(Locale.ROOT);
        return !isIPv4Literal(lower) && !isLikelyIPv6Literal(lower) && !"localhost".equals(lower) && !"ip6-localhost".equals(lower) && !"ip6-loopback".equals(lower) && !"docker-desktop".equals(lower) && !lower.contains("docker") && !lower.contains("wsl") && !lower.endsWith(".internal") && !lower.endsWith(".localdomain") && !lower.endsWith(".local");
    }
    
    private static boolean isIPv4Literal(@Nonnull final String name) {
        int dots = 0;
        int octet = -1;
        int val = 0;
        for (int i = 0; i < name.length(); ++i) {
            final char ch = name.charAt(i);
            if (ch >= '0' && ch <= '9') {
                if (octet == -1) {
                    octet = 0;
                }
                val = val * 10 + (ch - '0');
                if (val > 255) {
                    return false;
                }
                if (++octet > 3) {
                    return false;
                }
            }
            else {
                if (ch != '.') {
                    return false;
                }
                if (octet <= 0) {
                    return false;
                }
                ++dots;
                octet = -1;
                val = 0;
                if (dots > 3) {
                    return false;
                }
            }
        }
        return dots == 3 && octet > 0;
    }
    
    private static boolean isLikelyIPv6Literal(@Nonnull final String name) {
        boolean colon = false;
        for (int i = 0; i < name.length(); ++i) {
            final char ch = name.charAt(i);
            if (ch == ':') {
                colon = true;
            }
            else if ((ch < '0' || ch > '9') && (ch < 'a' || ch > 'f') && (ch < 'A' || ch > 'F')) {
                return false;
            }
        }
        return colon;
    }
    
    static {
        try {
            NetworkUtil.ANY_IPV6_ADDRESS = Inet6Address.getByAddress("::", new byte[16], null);
            NetworkUtil.ANY_IPV4_ADDRESS = (Inet4Address)InetAddress.getByAddress("0.0.0.0", new byte[4]);
            NetworkUtil.LOOPBACK_IPV6_ADDRESS = Inet6Address.getByAddress("::1", new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, null);
            NetworkUtil.LOOPBACK_IPV4_ADDRESS = (Inet4Address)InetAddress.getByAddress("127.0.0.1", new byte[] { 127, 0, 0, 1 });
        }
        catch (final UnknownHostException e) {
            throw new RuntimeException(e);
        }
    }
    
    public enum AddressType
    {
        MULTICAST(InetAddress::isMulticastAddress), 
        ANY_LOCAL(InetAddress::isAnyLocalAddress), 
        LOOPBACK(InetAddress::isLoopbackAddress), 
        LINK_LOCAL(InetAddress::isLinkLocalAddress), 
        SITE_LOCAL(InetAddress::isSiteLocalAddress), 
        MC_GLOBAL(InetAddress::isMCGlobal), 
        MC_SITE_LOCAL(InetAddress::isMCSiteLocal), 
        MC_ORG_LOCAL(InetAddress::isMCOrgLocal);
        
        private final Predicate<InetAddress> predicate;
        
        private AddressType(final Predicate<InetAddress> predicate) {
            this.predicate = predicate;
        }
    }
}
