// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package slog

import (
	
	
	
	
	
	
	
	
)

// A Value can represent any Go value, but unlike type any,
// it can represent most small values without an allocation.
// The zero Value corresponds to nil.
type Value struct {
	_ [0]func() // disallow ==
	// num holds the value for Kinds Int64, Uint64, Float64, Bool and Duration,
	// the string length for KindString, and nanoseconds since the epoch for KindTime.
	num uint64
	// If any is of type Kind, then the value is in num as described above.
	// If any is of type *time.Location, then the Kind is Time and time.Time value
	// can be constructed from the Unix nanos in num and the location (monotonic time
	// is not preserved).
	// If any is of type stringptr, then the Kind is String and the string value
	// consists of the length in num and the pointer in any.
	// Otherwise, the Kind is Any and any is the value.
	// (This implies that Attrs cannot store values of type Kind, *time.Location
	// or stringptr.)
	any any
}

type (
	stringptr *byte // used in Value.any when the Value is a string
	groupptr  *Attr // used in Value.any when the Value is a []Attr
)

// Kind is the kind of a [Value].
type Kind int

// The following list is sorted alphabetically, but it's also important that
// KindAny is 0 so that a zero Value represents nil.

const (
	KindAny Kind = iota
	KindBool
	KindDuration
	KindFloat64
	KindInt64
	KindString
	KindTime
	KindUint64
	KindGroup
	KindLogValuer
)

var kindStrings = []string{
	"Any",
	"Bool",
	"Duration",
	"Float64",
	"Int64",
	"String",
	"Time",
	"Uint64",
	"Group",
	"LogValuer",
}

func ( Kind) () string {
	if  >= 0 && int() < len(kindStrings) {
		return kindStrings[]
	}
	return "<unknown slog.Kind>"
}

// Unexported version of Kind, just so we can store Kinds in Values.
// (No user-provided value has this type.)
type kind Kind

// Kind returns v's Kind.
func ( Value) () Kind {
	switch x := .any.(type) {
	case Kind:
		return 
	case stringptr:
		return KindString
	case timeLocation:
		return KindTime
	case groupptr:
		return KindGroup
	case LogValuer:
		return KindLogValuer
	case kind: // a kind is just a wrapper for a Kind
		return KindAny
	default:
		return KindAny
	}
}

//////////////// Constructors

// StringValue returns a new [Value] for a string.
func ( string) Value {
	return Value{num: uint64(len()), any: stringptr(unsafe.StringData())}
}

// IntValue returns a [Value] for an int.
func ( int) Value {
	return Int64Value(int64())
}

// Int64Value returns a [Value] for an int64.
func ( int64) Value {
	return Value{num: uint64(), any: KindInt64}
}

// Uint64Value returns a [Value] for a uint64.
func ( uint64) Value {
	return Value{num: , any: KindUint64}
}

// Float64Value returns a [Value] for a floating-point number.
func ( float64) Value {
	return Value{num: math.Float64bits(), any: KindFloat64}
}

// BoolValue returns a [Value] for a bool.
func ( bool) Value {
	 := uint64(0)
	if  {
		 = 1
	}
	return Value{num: , any: KindBool}
}

// Unexported version of *time.Location, just so we can store *time.Locations in
// Values. (No user-provided value has this type.)
type timeLocation *time.Location

// TimeValue returns a [Value] for a [time.Time].
// It discards the monotonic portion.
func ( time.Time) Value {
	if .IsZero() {
		// UnixNano on the zero time is undefined, so represent the zero time
		// with a nil *time.Location instead. time.Time.Location method never
		// returns nil, so a Value with any == timeLocation(nil) cannot be
		// mistaken for any other Value, time.Time or otherwise.
		return Value{any: timeLocation(nil)}
	}
	return Value{num: uint64(.UnixNano()), any: timeLocation(.Location())}
}

// DurationValue returns a [Value] for a [time.Duration].
func ( time.Duration) Value {
	return Value{num: uint64(.Nanoseconds()), any: KindDuration}
}

// GroupValue returns a new [Value] for a list of Attrs.
// The caller must not subsequently mutate the argument slice.
func ( ...Attr) Value {
	// Remove empty groups.
	// It is simpler overall to do this at construction than
	// to check each Group recursively for emptiness.
	if  := countEmptyGroups();  > 0 {
		 := make([]Attr, 0, len()-)
		for ,  := range  {
			if !.Value.isEmptyGroup() {
				 = append(, )
			}
		}
		 = 
	}
	return Value{num: uint64(len()), any: groupptr(unsafe.SliceData())}
}

// countEmptyGroups returns the number of empty group values in its argument.
func countEmptyGroups( []Attr) int {
	 := 0
	for ,  := range  {
		if .Value.isEmptyGroup() {
			++
		}
	}
	return 
}

// AnyValue returns a [Value] for the supplied value.
//
// If the supplied value is of type Value, it is returned
// unmodified.
//
// Given a value of one of Go's predeclared string, bool, or
// (non-complex) numeric types, AnyValue returns a Value of kind
// [KindString], [KindBool], [KindUint64], [KindInt64], or [KindFloat64].
// The width of the original numeric type is not preserved.
//
// Given a [time.Time] or [time.Duration] value, AnyValue returns a Value of kind
// [KindTime] or [KindDuration]. The monotonic time is not preserved.
//
// For nil, or values of all other types, including named types whose
// underlying type is numeric, AnyValue returns a value of kind [KindAny].
func ( any) Value {
	switch v := .(type) {
	case string:
		return StringValue()
	case int:
		return Int64Value(int64())
	case uint:
		return Uint64Value(uint64())
	case int64:
		return Int64Value()
	case uint64:
		return Uint64Value()
	case bool:
		return BoolValue()
	case time.Duration:
		return DurationValue()
	case time.Time:
		return TimeValue()
	case uint8:
		return Uint64Value(uint64())
	case uint16:
		return Uint64Value(uint64())
	case uint32:
		return Uint64Value(uint64())
	case uintptr:
		return Uint64Value(uint64())
	case int8:
		return Int64Value(int64())
	case int16:
		return Int64Value(int64())
	case int32:
		return Int64Value(int64())
	case float64:
		return Float64Value()
	case float32:
		return Float64Value(float64())
	case []Attr:
		return GroupValue(...)
	case Kind:
		return Value{any: kind()}
	case Value:
		return 
	default:
		return Value{any: }
	}
}

//////////////// Accessors

// Any returns v's value as an any.
func ( Value) () any {
	switch .Kind() {
	case KindAny:
		if ,  := .any.(kind);  {
			return Kind()
		}
		return .any
	case KindLogValuer:
		return .any
	case KindGroup:
		return .group()
	case KindInt64:
		return int64(.num)
	case KindUint64:
		return .num
	case KindFloat64:
		return .float()
	case KindString:
		return .str()
	case KindBool:
		return .bool()
	case KindDuration:
		return .duration()
	case KindTime:
		return .time()
	default:
		panic(fmt.Sprintf("bad kind: %s", .Kind()))
	}
}

// String returns Value's value as a string, formatted like [fmt.Sprint]. Unlike
// the methods Int64, Float64, and so on, which panic if v is of the
// wrong kind, String never panics.
func ( Value) () string {
	if ,  := .any.(stringptr);  {
		return unsafe.String(, .num)
	}
	var  []byte
	return string(.append())
}

func ( Value) () string {
	return unsafe.String(.any.(stringptr), .num)
}

// Int64 returns v's value as an int64. It panics
// if v is not a signed integer.
func ( Value) () int64 {
	if ,  := .Kind(), KindInt64;  !=  {
		panic(fmt.Sprintf("Value kind is %s, not %s", , ))
	}
	return int64(.num)
}

// Uint64 returns v's value as a uint64. It panics
// if v is not an unsigned integer.
func ( Value) () uint64 {
	if ,  := .Kind(), KindUint64;  !=  {
		panic(fmt.Sprintf("Value kind is %s, not %s", , ))
	}
	return .num
}

// Bool returns v's value as a bool. It panics
// if v is not a bool.
func ( Value) () bool {
	if ,  := .Kind(), KindBool;  !=  {
		panic(fmt.Sprintf("Value kind is %s, not %s", , ))
	}
	return .bool()
}

func ( Value) () bool {
	return .num == 1
}

// Duration returns v's value as a [time.Duration]. It panics
// if v is not a time.Duration.
func ( Value) () time.Duration {
	if ,  := .Kind(), KindDuration;  !=  {
		panic(fmt.Sprintf("Value kind is %s, not %s", , ))
	}

	return .duration()
}

func ( Value) () time.Duration {
	return time.Duration(int64(.num))
}

// Float64 returns v's value as a float64. It panics
// if v is not a float64.
func ( Value) () float64 {
	if ,  := .Kind(), KindFloat64;  !=  {
		panic(fmt.Sprintf("Value kind is %s, not %s", , ))
	}

	return .float()
}

func ( Value) () float64 {
	return math.Float64frombits(.num)
}

// Time returns v's value as a [time.Time]. It panics
// if v is not a time.Time.
func ( Value) () time.Time {
	if ,  := .Kind(), KindTime;  !=  {
		panic(fmt.Sprintf("Value kind is %s, not %s", , ))
	}
	return .time()
}

func ( Value) () time.Time {
	 := .any.(timeLocation)
	if  == nil {
		return time.Time{}
	}
	return time.Unix(0, int64(.num)).In()
}

// LogValuer returns v's value as a LogValuer. It panics
// if v is not a LogValuer.
func ( Value) () LogValuer {
	return .any.(LogValuer)
}

// Group returns v's value as a []Attr.
// It panics if v's [Kind] is not [KindGroup].
func ( Value) () []Attr {
	if ,  := .any.(groupptr);  {
		return unsafe.Slice((*Attr)(), .num)
	}
	panic("Group: bad kind")
}

func ( Value) () []Attr {
	return unsafe.Slice((*Attr)(.any.(groupptr)), .num)
}

//////////////// Other

// Equal reports whether v and w represent the same Go value.
func ( Value) ( Value) bool {
	 := .Kind()
	 := .Kind()
	if  !=  {
		return false
	}
	switch  {
	case KindInt64, KindUint64, KindBool, KindDuration:
		return .num == .num
	case KindString:
		return .str() == .str()
	case KindFloat64:
		return .float() == .float()
	case KindTime:
		return .time().Equal(.time())
	case KindAny, KindLogValuer:
		return .any == .any // may panic if non-comparable
	case KindGroup:
		return slices.EqualFunc(.group(), .group(), Attr.Equal)
	default:
		panic(fmt.Sprintf("bad kind: %s", ))
	}
}

// isEmptyGroup reports whether v is a group that has no attributes.
func ( Value) () bool {
	if .Kind() != KindGroup {
		return false
	}
	// We do not need to recursively examine the group's Attrs for emptiness,
	// because GroupValue removed them when the group was constructed, and
	// groups are immutable.
	return len(.group()) == 0
}

// append appends a text representation of v to dst.
// v is formatted as with fmt.Sprint.
func ( Value) ( []byte) []byte {
	switch .Kind() {
	case KindString:
		return append(, .str()...)
	case KindInt64:
		return strconv.AppendInt(, int64(.num), 10)
	case KindUint64:
		return strconv.AppendUint(, .num, 10)
	case KindFloat64:
		return strconv.AppendFloat(, .float(), 'g', -1, 64)
	case KindBool:
		return strconv.AppendBool(, .bool())
	case KindDuration:
		return append(, .duration().String()...)
	case KindTime:
		return append(, .time().String()...)
	case KindGroup:
		return fmt.Append(, .group())
	case KindAny, KindLogValuer:
		return fmt.Append(, .any)
	default:
		panic(fmt.Sprintf("bad kind: %s", .Kind()))
	}
}

// A LogValuer is any Go value that can convert itself into a Value for logging.
//
// This mechanism may be used to defer expensive operations until they are
// needed, or to expand a single value into a sequence of components.
type LogValuer interface {
	LogValue() Value
}

const maxLogValues = 100

// Resolve repeatedly calls LogValue on v while it implements [LogValuer],
// and returns the result.
// If v resolves to a group, the group's attributes' values are not recursively
// resolved.
// If the number of LogValue calls exceeds a threshold, a Value containing an
// error is returned.
// Resolve's return value is guaranteed not to be of Kind [KindLogValuer].
func ( Value) () ( Value) {
	 := 
	defer func() {
		if  := recover();  != nil {
			 = AnyValue(fmt.Errorf("LogValue panicked\n%s", stack(3, 5)))
		}
	}()

	for  := 0;  < maxLogValues; ++ {
		if .Kind() != KindLogValuer {
			return 
		}
		 = .LogValuer().LogValue()
	}
	 := fmt.Errorf("LogValue called too many times on Value of type %T", .Any())
	return AnyValue()
}

func stack(,  int) string {
	 := make([]uintptr, +1)
	 := runtime.Callers(+1, )
	if  == 0 {
		return "(no stack)"
	}
	 := runtime.CallersFrames([:])
	var  strings.Builder
	 := 0
	for {
		,  := .Next()
		fmt.Fprintf(&, "called from %s (%s:%d)\n", .Function, .File, .Line)
		if ! {
			break
		}
		++
		if  >=  {
			fmt.Fprintf(&, "(rest of stack elided)\n")
			break
		}
	}
	return .String()
}