/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.
 */
package org.apache.nifi.util;

import java.security.SecureRandom;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * IMPORTANT: This component is not part of public API!
 * ====================================================
 * <p>
 * This component generates type-one UUID. It is used for generating ID of all
 * NiFi components. Giving the 128-bit UUID structure which consists of Least
 * Significant Bits (LSB) and Most Significant Bits (MSB) this component
 * provides support for generating and maintaining INCEPTION ID of the component
 * (MSB) as well as its INSTANCE ID (LSB).
 * </p>
 * <p>
 * It is also important to understand that while this component does seed itself
 * from current time which could be extracted from the resulting UUID via call
 * to {@link UUID#timestamp()} operation, one should not be relying on such time
 * as the exact time when such ID was generated since in the event the same time
 * is passed to one of the {@link #generateId()} operation it will be
 * incremented by 1 since the goal of this component to only ensure uniqueness
 * and type-one semantics where each UUID generated by this component is
 * comparable and each subsequent ID is > then previous ID.
 * </p>
 * <p>
 * For more details on how it is interacted with please see
 * org.apache.nifi.web.util.SnippetUtils as well as
 * org.apache.nifi.web.util.SnippetUtilsTest which contain additional
 * documentation on its usage as well as ID generation contracts defined in
 * NiFi.
 * </p>
 */
public class ComponentIdGenerator {
    private static final Logger logger = LoggerFactory.getLogger(ComponentIdGenerator.class);

    public static final Object lock = new Object();

    private static long lastTime;
    private static long clockSequence = 0;
    private static final SecureRandom randomGenerator = new SecureRandom();

    /**
     * Will generate unique time based UUID where the next UUID is always
     * greater then the previous.
     */
    public final static UUID generateId() {
        return generateId(System.currentTimeMillis());
    }

    /**
     *
     */
    public final static UUID generateId(long currentTime) {
        return generateId(currentTime, randomGenerator.nextLong(), true);
    }

    /**
     *
     */
    public final static UUID generateId(long msb, long lsb, boolean ensureUnique) {
        long time;

        synchronized (lock) {
            if (ensureUnique && msb <= lastTime) {
                msb = ++lastTime;
            }
            lastTime = msb;
        }

        time = msb;

        // low Time
        time = msb << 32;

        // mid Time
        time |= ((msb & 0xFFFF00000000L) >> 16);

        // hi Time
        time |= 0x1000 | ((msb >> 48) & 0x0FFF);

        long clockSequenceHi = clockSequence;
        clockSequenceHi <<= 48;
        lsb = clockSequenceHi | lsb;
        final UUID uuid = new UUID(time, lsb);
        logger.debug("Generating UUID {} for msb={}, lsb={}, ensureUnique={}", uuid, msb, lsb, ensureUnique);
        return uuid;
    }
}