package org.lable.util.uniqueid;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * Decorator for an {@link IDGenerator} that sleeps at least a millisecond between each invocation to guarantee ID
 * spread.
 * </p>
 * <p>
 * This is not normally necessary or desired, but can be useful when you want to generate several IDs, but you don't
 * want subsequent IDs to start with the same byte.
 * </p>
 * <p>
 * This class is of course significantly slower than using an undecorated generator.
 * </p>
 */
public class OnePerMillisecondDecorator implements IDGenerator {
    final IDGenerator generator;
    long previousInvocation = 0;

    protected OnePerMillisecondDecorator(IDGenerator generator) {
        this.generator = generator;
    }

    /**
     * Wrap an {@link IDGenerator} in a OnePerMillisecondDecorator.
     *
     * @param generator Generator to decorate.
     * @return The decorated generator.
     */
    public static IDGenerator decorate(IDGenerator generator) {
        return new OnePerMillisecondDecorator(generator);
    }

    @Override
    public byte[] generate() throws GeneratorException {
        long now = System.currentTimeMillis();
        while (previousInvocation == now) {
            try {
                TimeUnit.MILLISECONDS.sleep(1);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            now = System.currentTimeMillis();
        }
        previousInvocation = now;
        return generator.generate();
    }

    @Override
    public Deque<byte[]> batch(int size) throws GeneratorException {
        Deque<byte[]> deck = new ArrayDeque<byte[]>();
        for (int i = 0; i < size; i++) {
            deck.add(generate());
        }
        return deck;
    }
}

@@ -682,16 +682,44 @@
 ion = 0;
+%0A    byte%5B%5D previous = null;
 %0A%0A    pr
@@ -1191,32 +1191,294 @@
 atorException %7B%0A
+        // Wait a millisecond (or two) until the current timestamp is not the same as the next.%0A        // Because the first byte is the last byte (reversed) of the current timestamp, the timestamps%0A        // have to differ to guarantee a different byte there.%0A
         long now
@@ -1568,67 +1568,80 @@
     
-try %7B%0A                TimeUnit.MILLISECONDS.sleep(1
+sleepAMillisecond();%0A            now = System.currentTimeMillis(
 );%0A     
     
@@ -1640,98 +1640,364 @@
     
+   %7D%0A
        
-%7D catch (InterruptedException e) %7B%0A                Thread.currentThread().interrupt
+ previousInvocation = now;%0A%0A        // The above trick fails in rare cases, so perform an additional check to guarantee the desired%0A        // result.%0A        byte%5B%5D id = generator.generate();%0A        if (previous != null) %7B%0A            while (previous%5B0%5D == id%5B0%5D) %7B%0A                sleepAMillisecond();%0A                id = generator.generate
 ();%0A
@@ -2022,126 +2022,257 @@
     
+%7D%0A%0A
     
-now = System.currentTimeMillis();%0A        %7D%0A        previousInvocation = now;%0A        return generator.generate();
+    previous = id;%0A        return id;%0A    %7D%0A%0A    private void sleepAMillisecond() %7B%0A        try %7B%0A            TimeUnit.MILLISECONDS.sleep(1);%0A        %7D catch (InterruptedException e) %7B%0A            Thread.currentThread().interrupt();%0A        %7D
 %0A   

Fix edge case with OnePerMillisecondDecorator