﻿B4J=true
Group=Default Group
ModulesStructureVersion=1
Type=Class
Version=10.3
@EndOfDesignText@
Sub Class_Globals
	Private Const HEXCHARS As String = "0123456789abcdef"
	Private Const RANDOM_LENGTH As Int = 10

	Private lastTimestamp As Long = 0
	Private lastRandom(RANDOM_LENGTH) As Byte

	#If B4A or B4J
	Private sr As JavaObject
    #End If
End Sub

Public Sub Initialize
    #If B4A or B4J
	sr.InitializeNewInstance("java.security.SecureRandom", Null)
    #End If
End Sub

Public Sub Generate(ts As Long) As String
	Dim sb As StringBuilder
	sb.Initialize
	
	If ts = 0 Then ts = DateTime.Now
	If ts < lastTimestamp Then ts = lastTimestamp
	
	Dim bytes(16) As Byte
	
	'Timestamp 48 bits
	bytes(0) = Bit.And(ts / 1099511627776, 0xFF)  
	bytes(1) = Bit.And(ts / 4294967296, 0xFF)
	bytes(2) = Bit.And(ts / 16777216, 0xFF)
	bytes(3) = Bit.And(ts / 65536, 0xFF)
	bytes(4) = Bit.And(ts / 256, 0xFF)
	bytes(5) = Bit.And(ts, 0xFF)
	
	'Random / Monotonic
	If ts = lastTimestamp Then
		IncrementRandom
	Else
		lastRandom = SecureRandom(RANDOM_LENGTH)
		lastTimestamp = ts
	End If
    
	For i = 0 To 9
		bytes(i + 6) = lastRandom(i)
	Next
    
	'Set version 7
	bytes(6) = Bit.Or(Bit.And(bytes(6), 0x0F), 0x70)
    
	'Set RFC 4122 variant
	bytes(8) = Bit.Or(Bit.And(bytes(8), 0x3F), 0x80)
    
	For i = 0 To bytes.Length - 1
		sb.Append(ByteToHex(bytes(i)))
		If i = 3 Or i = 5 Or i = 7 Or i = 9 Then 
			sb.Append("-")
		End If
	Next
    
	Return sb.ToString.ToLowerCase
End Sub

Private Sub SecureRandom(cnt As Int) As Byte()
	Dim rndSecure(cnt) As Byte
    #If B4A or B4J
	sr.RunMethod("nextBytes", Array(rndSecure))
    #Else If B4i
    For i = 0 To cnt - 1
        rndSecure(i) = Rnd(0, 256)
    Next
    #End If
	Return rndSecure
End Sub

Private Sub IncrementRandom
	For i = lastRandom.Length - 1 To 0 Step -1
		Dim value As Int = Bit.And(lastRandom(i), 0xFF)
		If value < 255 Then
			lastRandom(i) = value + 1
			Exit
		Else
			lastRandom(i) = 0
		End If
	Next
End Sub

Private Sub ByteToHex(bt As Byte) As String
	Dim unsignedByte As Int = Bit.And(bt, 0xFF)
	Dim highNibble As Int = Bit.ShiftRight(unsignedByte, 4)
	Dim lowNibble As Int = Bit.And(unsignedByte, 15)
	Return HEXCHARS.CharAt(highNibble) & HEXCHARS.CharAt(lowNibble)
End Sub
