package ksuid

import (
	
	
	
	
	
	
	
	
	
)

const (
	// KSUID's epoch starts more recently so that the 32-bit number space gives a
	// significantly higher useful lifetime of around 136 years from March 2017.
	// This number (14e8) was picked to be easy to remember.
	epochStamp int64 = 1400000000

	// Timestamp is a uint32
	timestampLengthInBytes = 4

	// Payload is 16-bytes
	payloadLengthInBytes = 16

	// KSUIDs are 20 bytes when binary encoded
	byteLength = timestampLengthInBytes + payloadLengthInBytes

	// The length of a KSUID when string (base62) encoded
	stringEncodedLength = 27

	// A string-encoded minimum value for a KSUID
	minStringEncoded = "000000000000000000000000000"

	// A string-encoded maximum value for a KSUID
	maxStringEncoded = "aWgEPTl1tmebfsQzFP4bxwgy80V"
)

// KSUIDs are 20 bytes:
//  00-03 byte: uint32 BE UTC timestamp with custom epoch
//  04-19 byte: random "payload"
type KSUID [byteLength]byte

var (
	rander     = rand.Reader
	randMutex  = sync.Mutex{}
	randBuffer = [payloadLengthInBytes]byte{}

	errSize        = fmt.Errorf("Valid KSUIDs are %v bytes", byteLength)
	errStrSize     = fmt.Errorf("Valid encoded KSUIDs are %v characters", stringEncodedLength)
	errStrValue    = fmt.Errorf("Valid encoded KSUIDs are bounded by %s and %s", minStringEncoded, maxStringEncoded)
	errPayloadSize = fmt.Errorf("Valid KSUID payloads are %v bytes", payloadLengthInBytes)

	// Represents a completely empty (invalid) KSUID
	Nil KSUID
	// Represents the highest value a KSUID can have
	Max = KSUID{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}
)

// Append appends the string representation of i to b, returning a slice to a
// potentially larger memory area.
func ( KSUID) ( []byte) []byte {
	return fastAppendEncodeBase62(, [:])
}

// The timestamp portion of the ID as a Time object
func ( KSUID) () time.Time {
	return correctedUTCTimestampToTime(.Timestamp())
}

// The timestamp portion of the ID as a bare integer which is uncorrected
// for KSUID's special epoch.
func ( KSUID) () uint32 {
	return binary.BigEndian.Uint32([:timestampLengthInBytes])
}

// The 16-byte random payload without the timestamp
func ( KSUID) () []byte {
	return [timestampLengthInBytes:]
}

// String-encoded representation that can be passed through Parse()
func ( KSUID) () string {
	return string(.Append(make([]byte, 0, stringEncodedLength)))
}

// Raw byte representation of KSUID
func ( KSUID) () []byte {
	// Safe because this is by-value
	return [:]
}

// IsNil returns true if this is a "nil" KSUID
func ( KSUID) () bool {
	return  == Nil
}

// Get satisfies the flag.Getter interface, making it possible to use KSUIDs as
// part of of the command line options of a program.
func ( KSUID) () interface{} {
	return 
}

// Set satisfies the flag.Value interface, making it possible to use KSUIDs as
// part of of the command line options of a program.
func ( *KSUID) ( string) error {
	return .UnmarshalText([]byte())
}

func ( KSUID) () ([]byte, error) {
	return []byte(.String()), nil
}

func ( KSUID) () ([]byte, error) {
	return .Bytes(), nil
}

func ( *KSUID) ( []byte) error {
	,  := Parse(string())
	if  != nil {
		return 
	}
	* = 
	return nil
}

func ( *KSUID) ( []byte) error {
	,  := FromBytes()
	if  != nil {
		return 
	}
	* = 
	return nil
}

// Value converts the KSUID into a SQL driver value which can be used to
// directly use the KSUID as parameter to a SQL query.
func ( KSUID) () (driver.Value, error) {
	if .IsNil() {
		return nil, nil
	}
	return .String(), nil
}

// Scan implements the sql.Scanner interface. It supports converting from
// string, []byte, or nil into a KSUID value. Attempting to convert from
// another type will return an error.
func ( *KSUID) ( interface{}) error {
	switch v := .(type) {
	case nil:
		return .scan(nil)
	case []byte:
		return .scan()
	case string:
		return .scan([]byte())
	default:
		return fmt.Errorf("Scan: unable to scan type %T into KSUID", )
	}
}

func ( *KSUID) ( []byte) error {
	switch len() {
	case 0:
		* = Nil
		return nil
	case byteLength:
		return .UnmarshalBinary()
	case stringEncodedLength:
		return .UnmarshalText()
	default:
		return errSize
	}
}

// Parse decodes a string-encoded representation of a KSUID object
func ( string) (KSUID, error) {
	if len() != stringEncodedLength {
		return Nil, errStrSize
	}

	 := [stringEncodedLength]byte{}
	 := [byteLength]byte{}

	copy([:], [:])

	if  := fastDecodeBase62([:], [:]);  != nil {
		return Nil, errStrValue
	}

	return FromBytes([:])
}

func timeToCorrectedUTCTimestamp( time.Time) uint32 {
	return uint32(.Unix() - epochStamp)
}

func correctedUTCTimestampToTime( uint32) time.Time {
	return time.Unix(int64()+epochStamp, 0)
}

// Generates a new KSUID. In the strange case that random bytes
// can't be read, it will panic.
func () KSUID {
	,  := NewRandom()
	if  != nil {
		panic(fmt.Sprintf("Couldn't generate KSUID, inconceivable! error: %v", ))
	}
	return 
}

// Generates a new KSUID
func () ( KSUID,  error) {
	return NewRandomWithTime(time.Now())
}

func ( time.Time) ( KSUID,  error) {
	// Go's default random number generators are not safe for concurrent use by
	// multiple goroutines, the use of the rander and randBuffer are explicitly
	// synchronized here.
	randMutex.Lock()

	_,  = io.ReadAtLeast(rander, randBuffer[:], len(randBuffer))
	copy([timestampLengthInBytes:], randBuffer[:])

	randMutex.Unlock()

	if  != nil {
		 = Nil // don't leak random bytes on error
		return
	}

	 := timeToCorrectedUTCTimestamp()
	binary.BigEndian.PutUint32([:timestampLengthInBytes], )
	return
}

// Constructs a KSUID from constituent parts
func ( time.Time,  []byte) (KSUID, error) {
	if len() != payloadLengthInBytes {
		return Nil, errPayloadSize
	}

	var  KSUID

	 := timeToCorrectedUTCTimestamp()
	binary.BigEndian.PutUint32([:timestampLengthInBytes], )

	copy([timestampLengthInBytes:], )

	return , nil
}

// Constructs a KSUID from a 20-byte binary representation
func ( []byte) (KSUID, error) {
	var  KSUID

	if len() != byteLength {
		return Nil, errSize
	}

	copy([:], )
	return , nil
}

// Sets the global source of random bytes for KSUID generation. This
// should probably only be set once globally. While this is technically
// thread-safe as in it won't cause corruption, there's no guarantee
// on ordering.
func ( io.Reader) {
	if  == nil {
		rander = rand.Reader
		return
	}
	rander = 
}

// Implements comparison for KSUID type
func (,  KSUID) int {
	return bytes.Compare([:], [:])
}

// Sorts the given slice of KSUIDs
func ( []KSUID) {
	quickSort(, 0, len()-1)
}

// IsSorted checks whether a slice of KSUIDs is sorted
func ( []KSUID) bool {
	if len() != 0 {
		 := [0]
		for ,  := range [1:] {
			if bytes.Compare([:], [:]) > 0 {
				return false
			}
			 = 
		}
	}
	return true
}

func quickSort( []KSUID,  int,  int) {
	if  <  {
		 := []
		 :=  - 1

		for ,  := , ;  != ; ++ {
			if bytes.Compare([][:], [:]) < 0 {
				++
				[], [] = [], []
			}
		}

		++
		if bytes.Compare([][:], [][:]) < 0 {
			[], [] = [], []
		}

		(, , -1)
		(, +1, )
	}
}

// Next returns the next KSUID after id.
func ( KSUID) () KSUID {
	 := makeUint128(0, 0)

	 := .Timestamp()
	 := uint128Payload()
	 := add128(, makeUint128(0, 1))

	if  ==  { // overflow
		++
	}

	return .ksuid()
}

// Prev returns the previoud KSUID before id.
func ( KSUID) () KSUID {
	 := makeUint128(math.MaxUint64, math.MaxUint64)

	 := .Timestamp()
	 := uint128Payload()
	 := sub128(, makeUint128(0, 1))

	if  ==  { // overflow
		--
	}

	return .ksuid()
}