// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.net;

import java.net.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

/**
 * Utilities for returning localhost addresses on Linux.
 * See 
 * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4665037
 * on why this is necessary.
 *
 * @author bratseth
 */
public class LinuxInetAddress {

    private static Logger log = Logger.getLogger(LinuxInetAddress.class.getName());

    private static boolean isGood(InetAddress addr) {
        if (addr.isLoopbackAddress()) return false;
        String asAddr = addr.getHostAddress();
        String asName = addr.getCanonicalHostName();
        return ! asAddr.equals(asName);
    }

    /**
     * Returns an InetAddress representing the address of the localhost.
     * A non-loopback address is preferred if available.
     * IPv4 is preferred over IPv6 if available.
     *
     * @return a localhost address
     * @throws UnknownHostException if an address could not be determined
     */
    public static InetAddress getLocalHost() throws UnknownHostException {
        InetAddress localAddress;
        try {
            localAddress = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            return InetAddress.getLoopbackAddress();
        }

        if (isGood(localAddress)) return localAddress;

        List<InetAddress> nonLoopbackAddresses = 
                getAllLocalFromNetwork().stream().filter(a -> isGood(a)).collect(Collectors.toList());
        if (nonLoopbackAddresses.isEmpty()) return localAddress;

        List<InetAddress> ipV4NonLoopbackAddresses = 
                nonLoopbackAddresses.stream().filter(a -> a instanceof Inet4Address).collect(Collectors.toList());
        if ( ! ipV4NonLoopbackAddresses.isEmpty()) return ipV4NonLoopbackAddresses.get(0);
        
        return nonLoopbackAddresses.get(0);
    }

    /**
     * Returns all local addresses of this host.
     *
     * @return an array of the addresses of this
     * @throws UnknownHostException if we cannot access the network
     */
    public static InetAddress[] getAllLocal() throws UnknownHostException {
        InetAddress[] localInetAddresses = InetAddress.getAllByName("127.0.0.1");
        if ( ! localInetAddresses[0].isLoopbackAddress()) return localInetAddresses;
        return getAllLocalFromNetwork().toArray(new InetAddress[0]);
    }

    /**
     * Returns all local addresses of this host.
     *
     * @return a list of the addresses of this
     * @throws UnknownHostException if we cannot access the network
     */
    private static List<InetAddress> getAllLocalFromNetwork() throws UnknownHostException {
        try {
            List<InetAddress> addresses = new ArrayList<>();
            for (NetworkInterface networkInterface : Collections.list(NetworkInterface.getNetworkInterfaces()))
                addresses.addAll(Collections.list(networkInterface.getInetAddresses()));
            return addresses;
        }
        catch (SocketException ex) {
            throw new UnknownHostException("127.0.0.1");
        }
    }

}

@@ -430,17 +430,16 @@
 .%0A * See
- 
 %0A * http
@@ -671,261 +671,8 @@
 );%0A%0A
-    private static boolean isGood(InetAddress addr) %7B%0A        if (addr.isLoopbackAddress()) return false;%0A        String asAddr = addr.getHostAddress();%0A        String asName = addr.getCanonicalHostName();%0A        return ! asAddr.equals(asName);%0A    %7D%0A%0A
     
@@ -801,24 +801,113 @@
  available.%0A
+     * An address that resolves to a hostname is preferred among non-loopback addresses.%0A
      * IPv4 
@@ -929,28 +929,41 @@
 er IPv6 
-if available
+among resolving addresses
 .%0A     *
@@ -1008,191 +1008,120 @@
    *
- @throws UnknownHostException if an address could not be determined%0A     */%0A    public static InetAddress getLocalHost() throws UnknownHostException %7B%0A        InetAddress local
+/%0A    public static InetAddress getLocalHost() %7B%0A        InetAddress fallback = InetAddress.getLoopback
 Address
+()
 ;%0A  
@@ -1144,16 +1144,28 @@
         
+InetAddress 
 localAdd
@@ -1211,42 +1211,36 @@
     
-%7D catch (UnknownHostException e) %7B
+    fallback = localAddress;
 %0A   
@@ -1248,23 +1248,21 @@
         
-return 
+List%3C
 InetAddr
@@ -1264,36 +1264,146 @@
 tAddress
-.getLoopbackAddress(
+%3E nonLoopback =%0A                getAllLocalFromNetwork().stream().filter(a -%3E ! a.isLoopbackAddress()).collect(Collectors.toList()
 );%0A     
@@ -1408,68 +1408,136 @@
     
- %7D%0A%0A
      
-   
 if (
-isGood(localAddress)) return localAddress;%0A%0A
+nonLoopback.isEmpty()) %7B%0A                return fallback;%0A            %7D%0A            fallback = nonLoopback.get(0);%0A    
     
@@ -1562,31 +1562,19 @@
 ss%3E 
-nonLoopbackAddresses
+resolving
  =
- 
 %0A   
@@ -1590,32 +1590,23 @@
     
-getAllLocalFromNetwork()
+    nonLoopback
 .str
@@ -1627,14 +1627,19 @@
  -%3E 
-isGood
+doesResolve
 (a))
@@ -1681,66 +1681,131 @@
     
-if (nonLoopbackAddresses.isEmpty()) return localAddress;%0A%0A
+    if (resolving.isEmpty()) %7B%0A                return fallback;%0A            %7D%0A            fallback = resolving.get(0);%0A    
     
@@ -1834,31 +1834,10 @@
 ipV4
-NonLoopbackAddresses
  =
- 
 %0A   
@@ -1849,36 +1849,29 @@
         
-nonLoopbackAddresses
+    resolving
 .stream(
@@ -1953,108 +1953,167 @@
     
+    
 if (
- ! 
 ipV4
-NonLoopbackAddresses.isEmpty()) return ipV4NonLoopbackAddresses.get(0);%0A        %0A
+.isEmpty()) %7B%0A                return fallback;%0A            %7D%0A            return ipV4.get(0);%0A        %7D catch (UnknownHostException e) %7B%0A   
         
 retu
@@ -2112,43 +2112,35 @@
     
+ 
 return 
-nonLoopbackAddresses.get(0);
+fallback;%0A        %7D
 %0A   
@@ -3357,10 +3357,216 @@
 %0A    %7D%0A%0A
+    private static boolean doesResolve(InetAddress addr) %7B%0A        String asAddr = addr.getHostAddress();%0A        String asName = addr.getCanonicalHostName();%0A        return ! asAddr.equals(asName);%0A    %7D%0A%0A
 %7D%0A

rewrite after review feedback