package utils

// smallInts contains precomputed string representations for small integers 0-99
var smallInts [100]string

// smallNegInts contains precomputed string representations for small negative integers -1 to -99
var smallNegInts [100]string

// uint8Strs contains precomputed string representations for all uint8 values.
var uint8Strs [256]string

// int8Strs contains precomputed string representations for all int8 values indexed by uint8(value).
var int8Strs [256]string

func init() {
	for i := range 100 {
		smallInts[i] = formatUintSmall(uint64(i))
		if i > 0 {
			smallNegInts[i] = "-" + smallInts[i]
		}
	}

	for i := range 256 {
		v := uint8(i)
		uint8Strs[i] = formatUint8Slow(v)

		sv := int8(i)
		if sv >= 0 {
			int8Strs[i] = uint8Strs[sv]
		} else {
			int8Strs[i] = "-" + uint8Strs[uint8(-sv)]
		}
	}
}

func formatUintSmall(n uint64) string {
	if n < 10 {
		return string(byte(n) + '0')
	}
	return string([]byte{byte(n/10) + '0', byte(n%10) + '0'})
}

func formatUint8Slow(n uint8) string {
	if n < 100 {
		return smallInts[n]
	}
	return string([]byte{n/100 + '0', (n/10)%10 + '0', n%10 + '0'})
}

// formatUintBuf writes the digits of n into buf from the end and returns the start index.
// buf must be at least 20 bytes.
func formatUintBuf(buf *[20]byte, n uint64) int {
	i := 20
	for n >= 10 {
		i--
		buf[i] = byte(n%10) + '0'
		n /= 10
	}
	i--
	buf[i] = byte(n) + '0'
	return i
}

// FormatUint formats a uint64 as a decimal string.
// It is faster than strconv.FormatUint for most inputs.
func FormatUint(n uint64) string {
	if n < 100 {
		return smallInts[n]
	}
	var buf [20]byte
	i := formatUintBuf(&buf, n)
	return string(buf[i:])
}

// FormatInt formats an int64 as a decimal string.
// It is faster than strconv.FormatInt for most inputs.
func FormatInt(n int64) string {
	if n >= 0 && n < 100 {
		return smallInts[n]
	}
	if n < 0 && n > -100 {
		return smallNegInts[-n]
	}
	if n >= 0 {
		var buf [20]byte
		i := formatUintBuf(&buf, uint64(n))
		return string(buf[i:])
	}
	var buf [20]byte
	i := formatUintBuf(&buf, uint64(-n))
	i--
	buf[i] = '-'
	return string(buf[i:])
}

// FormatUint32 formats a uint32 as a decimal string.
func FormatUint32(n uint32) string {
	if n < 100 {
		return smallInts[n]
	}
	var buf [10]byte // max 4294967295
	i := 10
	for n >= 10 {
		i--
		buf[i] = byte(n%10) + '0' //nolint:gosec // i is always in bounds: starts at 10, decrements max 10 times for uint32
		n /= 10
	}
	i--
	buf[i] = byte(n) + '0' //nolint:gosec // i is always >= 0 after loop
	return string(buf[i:])
}

// FormatInt32 formats an int32 as a decimal string.
func FormatInt32(n int32) string {
	if n >= 0 && n < 100 {
		return smallInts[n]
	}
	if n < 0 && n > -100 {
		return smallNegInts[-n]
	}
	if n >= 0 {
		return FormatUint32(uint32(n))
	}
	var buf [11]byte // max -2147483648
	un := uint32(-n)
	i := 11
	for un >= 10 {
		i--
		buf[i] = byte(un%10) + '0'
		un /= 10
	}
	i--
	buf[i] = byte(un) + '0'
	i--
	buf[i] = '-'
	return string(buf[i:])
}

// FormatUint16 formats a uint16 as a decimal string.
func FormatUint16(n uint16) string {
	if n < 100 {
		return smallInts[n]
	}
	var buf [5]byte // max 65535
	i := 5
	for n >= 10 {
		i--
		buf[i] = byte(n%10) + '0' //nolint:gosec // i is always in bounds: starts at 5, decrements max 5 times for uint16
		n /= 10
	}
	i--
	buf[i] = byte(n) + '0' //nolint:gosec // i is always >= 0 after loop
	return string(buf[i:])
}

// FormatInt16 formats an int16 as a decimal string.
func FormatInt16(n int16) string {
	if n >= 0 && n < 100 {
		return smallInts[n]
	}
	if n < 0 && n > -100 {
		return smallNegInts[-n]
	}
	if n >= 0 {
		return FormatUint16(uint16(n))
	}
	var buf [6]byte // max -32768
	un := uint16(-n)
	i := 6
	for un >= 10 {
		i--
		buf[i] = byte(un%10) + '0' //nolint:gosec // i is always in bounds
		un /= 10
	}
	i--
	buf[i] = byte(un) + '0' //nolint:gosec // i is always >= 1 after loop
	i--
	buf[i] = '-' //nolint:gosec // i is always >= 0 after decrement
	return string(buf[i:])
}

// FormatUint8 formats a uint8 as a decimal string.
func FormatUint8(n uint8) string {
	return uint8Strs[n]
}

// FormatInt8 formats an int8 as a decimal string.
func FormatInt8(n int8) string {
	return int8Strs[uint8(n)]
}

// AppendUint appends the decimal string representation of n to dst.
func AppendUint(dst []byte, n uint64) []byte {
	if n < 100 {
		return append(dst, smallInts[n]...)
	}
	var buf [20]byte
	i := formatUintBuf(&buf, n)
	return append(dst, buf[i:]...)
}

// AppendInt appends the decimal string representation of n to dst.
func AppendInt(dst []byte, n int64) []byte {
	if n >= 0 {
		return AppendUint(dst, uint64(n))
	}
	if n > -100 {
		return append(dst, smallNegInts[-n]...)
	}
	var buf [20]byte
	i := formatUintBuf(&buf, uint64(-n))
	i--
	buf[i] = '-' //nolint:gosec // i is always >= 0: formatUintBuf returns at least 1 for any input, so i >= 0 after decrement
	return append(dst, buf[i:]...)
}
