/*
 * 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;

/**
 * A Java implementation of ULID, as per https://github.com/alizain/ulid.
 */
public class ULID {
    /**
     * Table of characters used to base32-encode the ULID time and random components.
     */
    private static final char[] ENCODE_TABLE = {
        '0','1','2','3','4','5','6','7','8','9',
        'A','B','C','D','E','F','G','H','J','K',
        'M','N','P','Q','R','S','T','V','W','X',
        'Y','Z'
    };
    /**
     * The number of characters in the ULID devoted to the time component.
     */
    private static final int TIME_LENGTH = 10;
    /**
     * The number of characters in the ULID devoted ot the random component.
     */
    private static final int RANDOM_LENGTH = 16;

    /**
     * Returns a newly generated ULID.
     *
     * @return A newly generated ULID.
     */
    public static String generate() {
        StringBuilder buffer = new StringBuilder();
        encodeRandom(buffer);
        encodeTime(buffer);
        return buffer.reverse().toString();
    }

    /**
     * Encode the current time into this ULID's string buffer.
     */
    private static void encodeTime(StringBuilder buffer) {
        long currentTime = System.currentTimeMillis();

        for (int i = 0; i < TIME_LENGTH; i++) {
            int mod = (int)(currentTime % ENCODE_TABLE.length);
            currentTime = (currentTime - mod) / ENCODE_TABLE.length;
            buffer.append(ENCODE_TABLE[mod]);
        }
    }

    /**
     * Encodes a randomness component into this ULID's string buffer.
     */
    private static void encodeRandom(StringBuilder buffer) {
        for (int i = 0; i < RANDOM_LENGTH; i++) {
            int randomNumber = (int)(Math.floor(ENCODE_TABLE.length * Math.random()));
            buffer.append(ENCODE_TABLE[randomNumber]);
        }
    }
}

@@ -1875,16 +1875,166 @@
 TH = 16;
+%0A    /**%0A     * The multiplication factor used for 40-bits of randomness.%0A     */%0A    private static final double RANDOM_MULTIPLIER = Math.pow(2, 40);
 %0A%0A    /*
@@ -2346,62 +2346,291 @@
   * 
-E
+Base-32 e
 ncode
+s
  the 
-curre
+given value for the given length i
 nt
+o
  t
-ime into this ULID's string buffer
+he given buffer.%0A     *%0A     * @param buffer    The buffer to encode the value into.%0A     * @param value     The value to be encoded.%0A     * @param length    The number of base-32 characters the value is to be represented by
 .%0A  
@@ -2665,20 +2665,16 @@
 d encode
-Time
 (StringB
@@ -2690,67 +2690,35 @@
 ffer
-) %7B%0A        long currentTime = System.currentTimeMillis();%0A
+, long value, int length) %7B
 %0A   
@@ -2742,27 +2742,22 @@
  0; i %3C 
-TIME_LENGTH
+length
 ; i++) %7B
@@ -2777,30 +2777,30 @@
 int 
-mod = (int)(currentTim
+remainder = (int)(valu
 e %25 
@@ -2837,40 +2837,34 @@
     
-currentTime = (currentTime - mod
+value = (value - remainder
 ) / 
@@ -2923,19 +2923,25 @@
 E_TABLE%5B
-mod
+remainder
 %5D);%0A    
@@ -2978,32 +2978,25 @@
 code
-s a randomness component
+ the current time
  int
@@ -3020,24 +3020,98 @@
 ing buffer.%0A
+     *%0A     * @param buffer    The buffer into which the time is encoded.%0A
      */%0A    
@@ -3136,22 +3136,20 @@
 d encode
-Random
+Time
 (StringB
@@ -3177,201 +3177,469 @@
     
-for (int i = 0; i %3C RANDOM_LENGTH; i++) %7B
+encode(buffer, System.currentTimeMillis(), TIME_LENGTH);%0A    %7D%0A%0A    /**%0A     * Encodes a randomness component into this ULID's string buffer.
 %0A     
+*%0A
      
-  int
+* @param buffer    The buffer into which the
  random
-N
+ n
 umber 
-= (int)(Math.floor(ENCODE_TABLE.length * Math.random()));%0A            buffer.append(ENCODE_TABLE%5BrandomNumber%5D);%0A        %7D
+is encoded.%0A     */%0A    private static void encodeRandom(StringBuilder buffer) %7B%0A        encode(buffer, (long)(Math.random() * RANDOM_MULTIPLIER), RANDOM_LENGTH/2);%0A        encode(buffer, (long)(Math.random() * RANDOM_MULTIPLIER), RANDOM_LENGTH/2);
 %0A   

Refactor ULID.generate() to only call Math.random() twice to reduce thread synchronization overhead