// Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru>
//
// 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 uuid

import (
	
	
	
	
	
	
	
	
	
	
)

// Difference in 100-nanosecond intervals between
// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970).
const epochStart = 122192928000000000

// EpochFunc is the function type used to provide the current time.
type EpochFunc func() time.Time

// HWAddrFunc is the function type used to provide hardware (MAC) addresses.
type HWAddrFunc func() (net.HardwareAddr, error)

// DefaultGenerator is the default UUID Generator used by this package.
var DefaultGenerator Generator = NewGen()

// NewV1 returns a UUID based on the current timestamp and MAC address.
func () (UUID, error) {
	return DefaultGenerator.NewV1()
}

// NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name.
func ( UUID,  string) UUID {
	return DefaultGenerator.NewV3(, )
}

// NewV4 returns a randomly generated UUID.
func () (UUID, error) {
	return DefaultGenerator.NewV4()
}

// NewV5 returns a UUID based on SHA-1 hash of the namespace UUID and name.
func ( UUID,  string) UUID {
	return DefaultGenerator.NewV5(, )
}

// NewV6 returns a k-sortable UUID based on a timestamp and 48 bits of
// pseudorandom data. The timestamp in a V6 UUID is the same as V1, with the bit
// order being adjusted to allow the UUID to be k-sortable.
//
// This is implemented based on revision 03 of the Peabody UUID draft, and may
// be subject to change pending further revisions. Until the final specification
// revision is finished, changes required to implement updates to the spec will
// not be considered a breaking change. They will happen as a minor version
// releases until the spec is final.
func () (UUID, error) {
	return DefaultGenerator.NewV6()
}

// NewV7 returns a k-sortable UUID based on the current millisecond precision
// UNIX epoch and 74 bits of pseudorandom data. It supports single-node batch generation (multiple UUIDs in the same timestamp) with a Monotonic Random counter.
//
// This is implemented based on revision 04 of the Peabody UUID draft, and may
// be subject to change pending further revisions. Until the final specification
// revision is finished, changes required to implement updates to the spec will
// not be considered a breaking change. They will happen as a minor version
// releases until the spec is final.
func () (UUID, error) {
	return DefaultGenerator.NewV7()
}

// Generator provides an interface for generating UUIDs.
type Generator interface {
	NewV1() (UUID, error)
	NewV3(ns UUID, name string) UUID
	NewV4() (UUID, error)
	NewV5(ns UUID, name string) UUID
	NewV6() (UUID, error)
	NewV7() (UUID, error)
}

// Gen is a reference UUID generator based on the specifications laid out in
// RFC-4122 and DCE 1.1: Authentication and Security Services. This type
// satisfies the Generator interface as defined in this package.
//
// For consumers who are generating V1 UUIDs, but don't want to expose the MAC
// address of the node generating the UUIDs, the NewGenWithHWAF() function has been
// provided as a convenience. See the function's documentation for more info.
//
// The authors of this package do not feel that the majority of users will need
// to obfuscate their MAC address, and so we recommend using NewGen() to create
// a new generator.
type Gen struct {
	clockSequenceOnce sync.Once
	hardwareAddrOnce  sync.Once
	storageMutex      sync.Mutex

	rand io.Reader

	epochFunc     EpochFunc
	hwAddrFunc    HWAddrFunc
	lastTime      uint64
	clockSequence uint16
	hardwareAddr  [6]byte
}

// GenOption is a function type that can be used to configure a Gen generator.
type GenOption func(*Gen)

// interface check -- build will fail if *Gen doesn't satisfy Generator
var _ Generator = (*Gen)(nil)

// NewGen returns a new instance of Gen with some default values set. Most
// people should use this.
func () *Gen {
	return NewGenWithHWAF(defaultHWAddrFunc)
}

// NewGenWithHWAF builds a new UUID generator with the HWAddrFunc provided. Most
// consumers should use NewGen() instead.
//
// This is used so that consumers can generate their own MAC addresses, for use
// in the generated UUIDs, if there is some concern about exposing the physical
// address of the machine generating the UUID.
//
// The Gen generator will only invoke the HWAddrFunc once, and cache that MAC
// address for all the future UUIDs generated by it. If you'd like to switch the
// MAC address being used, you'll need to create a new generator using this
// function.
func ( HWAddrFunc) *Gen {
	return NewGenWithOptions(WithHWAddrFunc())
}

// NewGenWithOptions returns a new instance of Gen with the options provided.
// Most people should use NewGen() or NewGenWithHWAF() instead.
//
// To customize the generator, you can pass in one or more GenOption functions.
// For example:
//
//	gen := NewGenWithOptions(
//	    WithHWAddrFunc(myHWAddrFunc),
//	    WithEpochFunc(myEpochFunc),
//	    WithRandomReader(myRandomReader),
//	)
//
// NewGenWithOptions(WithHWAddrFunc(myHWAddrFunc)) is equivalent to calling
// NewGenWithHWAF(myHWAddrFunc)
// NewGenWithOptions() is equivalent to calling NewGen()
func ( ...GenOption) *Gen {
	 := &Gen{
		epochFunc:  time.Now,
		hwAddrFunc: defaultHWAddrFunc,
		rand:       rand.Reader,
	}

	for ,  := range  {
		()
	}

	return 
}

// WithHWAddrFunc is a GenOption that allows you to provide your own HWAddrFunc
// function.
// When this option is nil, the defaultHWAddrFunc is used.
func ( HWAddrFunc) GenOption {
	return func( *Gen) {
		if  == nil {
			 = defaultHWAddrFunc
		}

		.hwAddrFunc = 
	}
}

// WithEpochFunc is a GenOption that allows you to provide your own EpochFunc
// function.
// When this option is nil, time.Now is used.
func ( EpochFunc) GenOption {
	return func( *Gen) {
		if  == nil {
			 = time.Now
		}

		.epochFunc = 
	}
}

// WithRandomReader is a GenOption that allows you to provide your own random
// reader.
// When this option is nil, the default rand.Reader is used.
func ( io.Reader) GenOption {
	return func( *Gen) {
		if  == nil {
			 = rand.Reader
		}

		.rand = 
	}
}

// NewV1 returns a UUID based on the current timestamp and MAC address.
func ( *Gen) () (UUID, error) {
	 := UUID{}

	, ,  := .getClockSequence(false)
	if  != nil {
		return Nil, 
	}
	binary.BigEndian.PutUint32([0:], uint32())
	binary.BigEndian.PutUint16([4:], uint16(>>32))
	binary.BigEndian.PutUint16([6:], uint16(>>48))
	binary.BigEndian.PutUint16([8:], )

	,  := .getHardwareAddr()
	if  != nil {
		return Nil, 
	}
	copy([10:], )

	.SetVersion(V1)
	.SetVariant(VariantRFC4122)

	return , nil
}

// NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name.
func ( *Gen) ( UUID,  string) UUID {
	 := newFromHash(md5.New(), , )
	.SetVersion(V3)
	.SetVariant(VariantRFC4122)

	return 
}

// NewV4 returns a randomly generated UUID.
func ( *Gen) () (UUID, error) {
	 := UUID{}
	if ,  := io.ReadFull(.rand, [:]);  != nil {
		return Nil, 
	}
	.SetVersion(V4)
	.SetVariant(VariantRFC4122)

	return , nil
}

// NewV5 returns a UUID based on SHA-1 hash of the namespace UUID and name.
func ( *Gen) ( UUID,  string) UUID {
	 := newFromHash(sha1.New(), , )
	.SetVersion(V5)
	.SetVariant(VariantRFC4122)

	return 
}

// NewV6 returns a k-sortable UUID based on a timestamp and 48 bits of
// pseudorandom data. The timestamp in a V6 UUID is the same as V1, with the bit
// order being adjusted to allow the UUID to be k-sortable.
//
// This is implemented based on revision 03 of the Peabody UUID draft, and may
// be subject to change pending further revisions. Until the final specification
// revision is finished, changes required to implement updates to the spec will
// not be considered a breaking change. They will happen as a minor version
// releases until the spec is final.
func ( *Gen) () (UUID, error) {
	var  UUID

	if ,  := io.ReadFull(.rand, [10:]);  != nil {
		return Nil, 
	}

	, ,  := .getClockSequence(false)
	if  != nil {
		return Nil, 
	}

	binary.BigEndian.PutUint32([0:], uint32(>>28))   // set time_high
	binary.BigEndian.PutUint16([4:], uint16(>>12))   // set time_mid
	binary.BigEndian.PutUint16([6:], uint16(&0xfff)) // set time_low (minus four version bits)
	binary.BigEndian.PutUint16([8:], &0x3fff)       // set clk_seq_hi_res (minus two variant bits)

	.SetVersion(V6)
	.SetVariant(VariantRFC4122)

	return , nil
}

// getClockSequence returns the epoch and clock sequence for V1,V6 and V7 UUIDs.
//
//	When useUnixTSMs is false, it uses the Coordinated Universal Time (UTC) as a count of 100-
//
// nanosecond intervals since 00:00:00.00, 15 October 1582 (the date of Gregorian reform to the Christian calendar).
func ( *Gen) ( bool) (uint64, uint16, error) {
	var  error
	.clockSequenceOnce.Do(func() {
		 := make([]byte, 2)
		if _,  = io.ReadFull(.rand, );  != nil {
			return
		}
		.clockSequence = binary.BigEndian.Uint16()
	})
	if  != nil {
		return 0, 0, 
	}

	.storageMutex.Lock()
	defer .storageMutex.Unlock()

	var  uint64
	if  {
		 = uint64(.epochFunc().UnixMilli())
	} else {
		 = .getEpoch()
	}
	// Clock didn't change since last UUID generation.
	// Should increase clock sequence.
	if  <= .lastTime {
		.clockSequence++
	}
	.lastTime = 

	return , .clockSequence, nil
}

// NewV7 returns a k-sortable UUID based on the current millisecond precision
// UNIX epoch and 74 bits of pseudorandom data.
//
// This is implemented based on revision 04 of the Peabody UUID draft, and may
// be subject to change pending further revisions. Until the final specification
// revision is finished, changes required to implement updates to the spec will
// not be considered a breaking change. They will happen as a minor version
// releases until the spec is final.
func ( *Gen) () (UUID, error) {
	var  UUID
	/* https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-04.html#name-uuid-version-7
		0                   1                   2                   3
	    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	   |                           unix_ts_ms                          |
	   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	   |          unix_ts_ms           |  ver  |       rand_a          |
	   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	   |var|                        rand_b                             |
	   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	   |                            rand_b                             |
	   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */

	, ,  := .getClockSequence(true)
	if  != nil {
		return Nil, 
	}
	//UUIDv7 features a 48 bit timestamp. First 32bit (4bytes) represents seconds since 1970, followed by 2 bytes for the ms granularity.
	[0] = byte( >> 40) //1-6 bytes: big-endian unsigned number of Unix epoch timestamp
	[1] = byte( >> 32)
	[2] = byte( >> 24)
	[3] = byte( >> 16)
	[4] = byte( >> 8)
	[5] = byte()

	//support batching by using a monotonic pseudo-random sequence
	//The 6th byte contains the version and partially rand_a data.
	//We will lose the most significant bites from the clockSeq (with SetVersion), but it is ok, we need the least significant that contains the counter to ensure the monotonic property
	binary.BigEndian.PutUint16([6:8], ) // set rand_a with clock seq which is random and monotonic

	//override first 4bits of u[6].
	.SetVersion(V7)

	//set rand_b 64bits of pseudo-random bits (first 2 will be overridden)
	if _,  = io.ReadFull(.rand, [8:16]);  != nil {
		return Nil, 
	}
	//override first 2 bits of byte[8] for the variant
	.SetVariant(VariantRFC4122)

	return , nil
}

// Returns the hardware address.
func ( *Gen) () ([]byte, error) {
	var  error
	.hardwareAddrOnce.Do(func() {
		var  net.HardwareAddr
		if ,  = .hwAddrFunc();  == nil {
			copy(.hardwareAddr[:], )
			return
		}

		// Initialize hardwareAddr randomly in case
		// of real network interfaces absence.
		if _,  = io.ReadFull(.rand, .hardwareAddr[:]);  != nil {
			return
		}
		// Set multicast bit as recommended by RFC-4122
		.hardwareAddr[0] |= 0x01
	})
	if  != nil {
		return []byte{}, 
	}
	return .hardwareAddr[:], nil
}

// Returns the difference between UUID epoch (October 15, 1582)
// and current time in 100-nanosecond intervals.
func ( *Gen) () uint64 {
	return epochStart + uint64(.epochFunc().UnixNano()/100)
}

// Returns the UUID based on the hashing of the namespace UUID and name.
func newFromHash( hash.Hash,  UUID,  string) UUID {
	 := UUID{}
	.Write([:])
	.Write([]byte())
	copy([:], .Sum(nil))

	return 
}

var netInterfaces = net.Interfaces

// Returns the hardware address.
func defaultHWAddrFunc() (net.HardwareAddr, error) {
	,  := netInterfaces()
	if  != nil {
		return []byte{}, 
	}
	for ,  := range  {
		if len(.HardwareAddr) >= 6 {
			return .HardwareAddr, nil
		}
	}
	return []byte{}, fmt.Errorf("uuid: no HW address found")
}