/*
 * Copyright (c) 2008 JSCAPE
 * 1147 S. 53rd Pl., Mesa, Arizona, 85206, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * JSCAPE. ("Confidential Information").  You shall not disclose such
 * Confidential Information and shall use it only in accordance with
 * the terms of the license agreement you entered into with JSCAPE.
 *
 */

package com.jscape.util.net;

import com.jscape.util.ArrayTools;
import com.jscape.util.SuppressObfuscation;
import com.jscape.util.collections.CollectionAppender;
import com.jscape.util.collections.EnumerationIterator;
import com.jscape.util.collections.FilterIterator;
import com.jscape.util.functor.UnaryFunction;
import com.jscape.util.functor.UnaryPredicate;
import com.jscape.util.functor.adapters.Instance;

import java.net.*;
import java.util.*;

import static com.jscape.util.Parameters.assertNotNull;
import static com.jscape.util.collections.Algorithms.*;
import static com.jscape.util.collections.CollectionAppender.listAppender;
import static com.jscape.util.collections.TransformIterator.adapt;
import static com.jscape.util.collections.TransformIterator.iterator;

/**
 * @author Alex Usun
 */
@SuppressObfuscation
public class NetworkInterfaceInfo {

    private String name;
    private String displayName;
    private String[] addresses;

    public static NetworkInterfaceInfo[] getSystemInterfaces()
            throws SocketException {
        List<NetworkInterfaceInfo> interfaces = new LinkedList<>();

        for (Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces(); e.hasMoreElements(); ) {
            NetworkInterface candidate = e.nextElement();
            if (candidate.getInetAddresses().hasMoreElements()) {
                Collection<String> addresses = copy(iterator(candidate.getInetAddresses(), new HostAddress()));
                NetworkInterfaceInfo info = new NetworkInterfaceInfo(candidate.getName(), candidate.getDisplayName() != null ? candidate.getDisplayName() : candidate.getName(), addresses);
                interfaces.add(info);
            }
        }

        return interfaces.toArray(new NetworkInterfaceInfo[0]);
    }

    public static String[] getSystemInetAddresses()
            throws SocketException {
        return getSystemInetAddresses(new UnaryPredicate.True<InetAddress>());
    }

    public static String[] getSystemInet4Addresses()
            throws SocketException {
        return getSystemInetAddresses(new Instance<InetAddress>(Inet4Address.class));
    }

    public static String[] getSystemInet6Addresses()
            throws SocketException {
        return getSystemInetAddresses(new Instance<InetAddress>(Inet6Address.class));
    }

    public static String[] getSystemInetAddresses(UnaryPredicate<InetAddress> predicate)
            throws SocketException {
        CollectionAppender<String> appender = listAppender();

        for (Iterator<NetworkInterface> i = EnumerationIterator.iterator(NetworkInterface.getNetworkInterfaces()); i.hasNext(); ) {
            forEach(adapt(FilterIterator.iterator(i.next().getInetAddresses(), predicate), new HostAddress()), appender);
        }

        return toArray(appender.getCollection(), String.class);
    }

    public NetworkInterfaceInfo(String name, String displayName, String[] addresses) {
        setName(name);
        setDisplayName(displayName != null ? displayName : name);
        setAddresses(addresses);
    }

    public NetworkInterfaceInfo(String name, String displayName, Collection<String> addresses) {
        this(name, displayName, addresses.toArray(new String[0]));
    }

    public NetworkInterfaceInfo() {
        // empty
    }

    public String getName() {
        return this.name;
    }

    public void setName(String value) {
        assertNotNull(value);
        this.name = value;
    }

    public String getDisplayName() {
        return this.displayName;
    }

    public void setDisplayName(String value) {
        assertNotNull(value);
        this.displayName = value;
    }

    public String[] getAddresses() {
        return this.addresses;
    }

    public void setAddresses(String[] value) {
        assertNotNull(value);
        this.addresses = value;
    }

    public String toString() {
        return "NetworkInterfaceInfo {"
               + "name='" + this.name + '\''
               + ", displayName='" + this.displayName + '\''
               + ", addresses=" + ArrayTools.toString(this.addresses)
               + "}";
    }

    private static class HostAddress
            extends UnaryFunction<InetAddress, String> {

        public String evaluate(InetAddress value) {
            return value.getHostAddress();
        }
    }

    private static class InfoGenerator
            extends UnaryFunction<NetworkInterface, NetworkInterfaceInfo> {

        public NetworkInterfaceInfo evaluate(NetworkInterface value) {
            Collection<String> addresses = copy(iterator(value.getInetAddresses(), new HostAddress()));
            return new NetworkInterfaceInfo(value.getName(), value.getDisplayName() != null ? value.getDisplayName() : value.getName(), addresses);
        }
    }
}
