/*
 *  ******************************************************************************
 *  *
 *  *
 *  * This program and the accompanying materials are made available under the
 *  * terms of the Apache License, Version 2.0 which is available at
 *  * https://www.apache.org/licenses/LICENSE-2.0.
 *  *
 *  *  See the NOTICE file distributed with this work for additional
 *  *  information regarding copyright ownership.
 *  * Unless required by applicable law or agreed to in writing, software
 *  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 *  * License for the specific language governing permissions and limitations
 *  * under the License.
 *  *
 *  * SPDX-License-Identifier: Apache-2.0
 *  *****************************************************************************
 */

package org.deeplearning4j.core.util;

import lombok.extern.slf4j.Slf4j;

import java.net.NetworkInterface;
import java.rmi.server.UID;
import java.util.Enumeration;

@Slf4j
public class UIDProvider {

    private static final String JVM_UID;
    private static final String HARDWARE_UID;

    static {

        UID jvmUIDSource = new UID();
        String asString = jvmUIDSource.toString();
        //Format here: hexStringFromRandomNumber:hexStringFromSystemClock:hexStringOfUIDInstance
        //The first two components here will be identical for all UID instances in a JVM, where as the 'hexStringOfUIDInstance'
        // will vary (increment) between UID object instances. So we'll only be using the first two components here
        int lastIdx = asString.lastIndexOf(":");
        JVM_UID = asString.substring(0, lastIdx).replaceAll(":", "");


        //Assumptions here:
        //1. getNetworkInterfaces() returns at least one non-null element
        //   This is guaranteed by getNetworkInterfaces() Javadoc: "The {@code Enumeration} contains at least one element..."
        //2. That the iteration order for network interfaces is consistent between JVM instances on the same hardware
        //   This appears to hold, but no formal guarantees seem to be available here
        //3. That MAC addresses are 'unique enough' for our purposes
        byte[] address = null;
        boolean noInterfaces = false;
        Enumeration<NetworkInterface> niEnumeration = null;
        try {
            niEnumeration = NetworkInterface.getNetworkInterfaces();
        } catch (Exception e) {
            noInterfaces = true;
        }

        if (niEnumeration != null) {
            while (niEnumeration.hasMoreElements()) {
                NetworkInterface ni = niEnumeration.nextElement();
                byte[] addr;
                try {
                    addr = ni.getHardwareAddress();
                } catch (Exception e) {
                    continue;
                }
                if (addr == null || addr.length != 6)
                    continue; //May be null (if it can't be obtained) or not standard 6 byte MAC-48 representation

                address = addr;
                break;
            }
        }

        if (address == null) {
            log.warn("Could not generate hardware UID{}. Using fallback: JVM UID as hardware UID.",
                            (noInterfaces ? " (no interfaces)" : ""));
            HARDWARE_UID = JVM_UID;
        } else {
            StringBuilder sb = new StringBuilder();
            for (byte b : address) {
                sb.append(String.format("%02x", b));
            }
            HARDWARE_UID = sb.toString();
        }
    }

    private UIDProvider() {}


    public static String getJVMUID() {
        return JVM_UID;
    }

    public static String getHardwareUID() {
        return HARDWARE_UID;
    }



}
