/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Lachlan Dowding
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package permafrost.tundra.id;

import java.util.concurrent.TimeUnit;

/**
 * Provides a time-based algorithm for generating integer IDs. The IDs generated are lexically ordered by time,
 * and fit within the bounds of a positive signed 32-bit integer.
 */
public class IntegerID {
    /**
     * The default time unit used to generate IDs.
     */
    private static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS;
    /**
     * The previously generated ID's time component, used to ensure only one ID is generated per second.
     */
    private long previous = -1;
    /**
     * The epoch from which time is measured.
     */
    private long epoch;
    /**
     * The divisor used to calculate the time component of the ID (time % divisor).
     */
    private int timeDivisor;
    /**
     * The number of bits used for partitioning the ID space.
     */
    private int partitionBitLength;
    /**
     * The value used to partition the ID space.
     */
    private int partition;
    /**
     * The unit of time used for calculating the time component of the ID.
     */
    private TimeUnit timeUnit;
    /**
     * The number of milliseconds in the specified time unit.
     */
    private long millisecondsInTimeUnit;

    /**
     * Constructs a new ID generator using 31 bits of time measured in seconds since the Unix epoch.
     */
    public IntegerID() {
        this(DEFAULT_TIME_UNIT);
    }

    /**
     * Constructs a new ID generator using 31 bits of time measured in the given time unit since the Unix epoch.
     *
     * @param timeUnit  The time unit used for generating IDs.
     */
    public IntegerID(TimeUnit timeUnit) {
        this(0, timeUnit);
    }

    /**
     * Constructs a new ID generator using 31 bits of time since the given epoch in the given time unit.
     *
     * @param epoch     The epoch from which time is measured.
     * @param timeUnit  The time unit used for generating IDs.
     */
    public IntegerID(long epoch, TimeUnit timeUnit) {
        this(epoch, timeUnit, 0, Integer.SIZE - 1);
    }

    /**
     * Constructs a new ID generator using the given number of bits for the partition component, the remaining number
     * of bits from 31 for the time component with time measured from Unix epoch in seconds.
     *
     * @param partitionBitLength    The number of bits used for partitioning the ID space.
     * @param partition             The value to use to partition the ID space.
     */
    public IntegerID(int partitionBitLength, int partition) {
        this(DEFAULT_TIME_UNIT, partitionBitLength, partition);
    }

    /**
     * Constructs a new ID generator using the given number of bits for the partition component, the remaining number
     * of bits from 31 for the time component with time measured from Unix epoch in the given time unit.
     *
     * @param timeUnit              The time unit used for generating IDs.
     * @param partitionBitLength    The number of bits used for partitioning the ID space.
     * @param partition             The value to use to partition the ID space.
     */
    public IntegerID(TimeUnit timeUnit, int partitionBitLength, int partition) {
        this(0, timeUnit, partitionBitLength, partition);
    }

    /**
     * Constructs a new ID generator using the given number of bits for the partition component, the remaining number
     * of bits from 31 for the time component with time measured from the given epoch in the given time unit.
     *
     * @param epoch                 The epoch from which time is measured.
     * @param timeUnit              The time unit used for generating IDs.
     * @param partitionBitLength    The number of bits used for partitioning the ID space.
     * @param partition             The value to use to partition the ID space.
     */
    public IntegerID(long epoch, TimeUnit timeUnit, int partitionBitLength, int partition) {
        if (epoch < 0) throw new IllegalArgumentException("epoch must be greater than or equal to zero");
        if (partitionBitLength <= 0) throw new IllegalArgumentException("partitionBitLength must be greater than or equal to zero");
        if (partition < 0) throw new IllegalArgumentException("partition must be greater than or equal to zero");
        if (partitionBitLength > (Integer.SIZE - 1)) throw new IllegalArgumentException("partitionBitLength must be less than or equal to " + (Integer.SIZE - 1));

        this.epoch = epoch;
        this.partitionBitLength = partitionBitLength;
        int partitionDivisor = (int)Math.pow(2, partitionBitLength);
        this.partition = partitionDivisor == 0 ? 0 : partition % partitionDivisor;
        this.timeDivisor = (int)Math.pow(2, ((Integer.SIZE - 1) - partitionBitLength)) - 1;
        this.timeUnit = timeUnit;
        this.millisecondsInTimeUnit = timeUnit.toMillis(1);
    }

    /**
     * Returns a newly generated ID.
     *
     * @return A newly generated ID.
     */
    public synchronized int generate() {
        long next;

        do {
            next = currentTime();
            next = previous == -1 ? next : Math.min(previous + 1, next);

            if (next == previous && millisecondsInTimeUnit > 0) {
                try {
                    Thread.sleep(millisecondsInTimeUnit - (System.currentTimeMillis() % millisecondsInTimeUnit));
                } catch (InterruptedException ex) {
                    throw new RuntimeException(ex);
                }
            }
        } while (next == previous);

        previous = next;

        return generate(next);
    }

    /**
     * Returns a new ID given a time value.
     *
     * @param time  The time to use to generate the new ID.
     * @return      A new ID.
     */
    private int generate(long time) {
        return (int)((time % timeDivisor) << partitionBitLength) | partition;
    }

    /**
     * Returns the current time since this generator's epoch in this generator's time unit.
     *
     * @return the current time since this generator's epoch in this generator's time unit.
     */
    private long currentTime() {
        return timeUnit.convert(System.currentTimeMillis() - epoch, TimeUnit.MILLISECONDS);
    }
}

@@ -2261,127 +2261,8 @@
 nit;
-%0A    /**%0A     * The number of milliseconds in the specified time unit.%0A     */%0A    private long millisecondsInTimeUnit;
 %0A%0A  
@@ -5805,68 +5805,8 @@
 it;%0A
-        this.millisecondsInTimeUnit = timeUnit.toMillis(1);%0A
     
@@ -6123,38 +6123,8 @@
 ious
- && millisecondsInTimeUnit %3E 0
 ) %7B%0A
@@ -6169,99 +6169,24 @@
     
-Thread.sleep(millisecondsInTimeUnit - (System.currentTimeMillis() %25 millisecondsInTimeUnit)
+timeUnit.sleep(1
 );%0A 

Refactor IntegerID.generate() to use TimeUnit.sleep() method rather than Thread.sleep() method