// Copyright 2019, 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 cmp

import (
	
	
	
	
	
	
	

	
)

var (
	anyType    = reflect.TypeOf((*interface{})(nil)).Elem()
	stringType = reflect.TypeOf((*string)(nil)).Elem()
	bytesType  = reflect.TypeOf((*[]byte)(nil)).Elem()
	byteType   = reflect.TypeOf((*byte)(nil)).Elem()
)

type formatValueOptions struct {
	// AvoidStringer controls whether to avoid calling custom stringer
	// methods like error.Error or fmt.Stringer.String.
	AvoidStringer bool

	// PrintAddresses controls whether to print the address of all pointers,
	// slice elements, and maps.
	PrintAddresses bool

	// QualifiedNames controls whether FormatType uses the fully qualified name
	// (including the full package path as opposed to just the package name).
	QualifiedNames bool

	// VerbosityLevel controls the amount of output to produce.
	// A higher value produces more output. A value of zero or lower produces
	// no output (represented using an ellipsis).
	// If LimitVerbosity is false, then the level is treated as infinite.
	VerbosityLevel int

	// LimitVerbosity specifies that formatting should respect VerbosityLevel.
	LimitVerbosity bool
}

// FormatType prints the type as if it were wrapping s.
// This may return s as-is depending on the current type and TypeMode mode.
func ( formatOptions) ( reflect.Type,  textNode) textNode {
	// Check whether to emit the type or not.
	switch .TypeMode {
	case autoType:
		switch .Kind() {
		case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map:
			if .Equal(textNil) {
				return 
			}
		default:
			return 
		}
		if .DiffMode == diffIdentical {
			return  // elide type for identical nodes
		}
	case elideType:
		return 
	}

	// Determine the type label, applying special handling for unnamed types.
	 := value.TypeString(, .QualifiedNames)
	if .Name() == "" {
		// According to Go grammar, certain type literals contain symbols that
		// do not strongly bind to the next lexicographical token (e.g., *T).
		switch .Kind() {
		case reflect.Chan, reflect.Func, reflect.Ptr:
			 = "(" +  + ")"
		}
	}
	return &textWrap{Prefix: , Value: wrapParens()}
}

// wrapParens wraps s with a set of parenthesis, but avoids it if the
// wrapped node itself is already surrounded by a pair of parenthesis or braces.
// It handles unwrapping one level of pointer-reference nodes.
func ( textNode) textNode {
	var  *textWrap
	if ,  := .(*textWrap);  {
		// Unwrap a single pointer reference node.
		switch .Metadata.(type) {
		case leafReference, trunkReference, trunkReferences:
			 = 
			if ,  := .Value.(*textWrap);  {
				 = 
			}
		}

		// Already has delimiters that make parenthesis unnecessary.
		 := strings.HasPrefix(.Prefix, "(") && strings.HasSuffix(.Suffix, ")")
		 := strings.HasPrefix(.Prefix, "{") && strings.HasSuffix(.Suffix, "}")
		if  ||  {
			return 
		}
	}
	if  != nil {
		.Value = &textWrap{Prefix: "(", Value: .Value, Suffix: ")"}
		return 
	}
	return &textWrap{Prefix: "(", Value: , Suffix: ")"}
}

// FormatValue prints the reflect.Value, taking extra care to avoid descending
// into pointers already in ptrs. As pointers are visited, ptrs is also updated.
func ( formatOptions) ( reflect.Value,  reflect.Kind,  *pointerReferences) ( textNode) {
	if !.IsValid() {
		return nil
	}
	 := .Type()

	// Check slice element for cycles.
	if  == reflect.Slice {
		,  := .Push(.Addr())
		if  {
			return makeLeafReference(, false)
		}
		defer .Pop()
		defer func() {  = wrapTrunkReference(, false, ) }()
	}

	// Check whether there is an Error or String method to call.
	if !.AvoidStringer && .CanInterface() {
		// Avoid calling Error or String methods on nil receivers since many
		// implementations crash when doing so.
		if (.Kind() != reflect.Ptr && .Kind() != reflect.Interface) || !.IsNil() {
			var ,  string
			func() {
				// Swallow and ignore any panics from String or Error.
				defer func() { recover() }()
				switch v := .Interface().(type) {
				case error:
					 = .Error()
					 = "e"
				case fmt.Stringer:
					 = .String()
					 = "s"
				}
			}()
			if  != "" {
				return .formatString(, )
			}
		}
	}

	// Check whether to explicitly wrap the result with the type.
	var  bool
	defer func() {
		if ! {
			 = .FormatType(, )
		}
	}()

	switch .Kind() {
	case reflect.Bool:
		return textLine(fmt.Sprint(.Bool()))
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return textLine(fmt.Sprint(.Int()))
	case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return textLine(fmt.Sprint(.Uint()))
	case reflect.Uint8:
		if  == reflect.Slice ||  == reflect.Array {
			return textLine(formatHex(.Uint()))
		}
		return textLine(fmt.Sprint(.Uint()))
	case reflect.Uintptr:
		return textLine(formatHex(.Uint()))
	case reflect.Float32, reflect.Float64:
		return textLine(fmt.Sprint(.Float()))
	case reflect.Complex64, reflect.Complex128:
		return textLine(fmt.Sprint(.Complex()))
	case reflect.String:
		return .formatString("", .String())
	case reflect.UnsafePointer, reflect.Chan, reflect.Func:
		return textLine(formatPointer(value.PointerOf(), true))
	case reflect.Struct:
		var  textList
		 := makeAddressable() // needed for retrieveUnexportedField
		 := .NumField()
		if .LimitVerbosity {
			 = ((1 << .verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc...
			.VerbosityLevel--
		}
		for  := 0;  < .NumField(); ++ {
			 := .Field()
			if .IsZero() {
				continue // Elide fields with zero values
			}
			if len() ==  {
				.AppendEllipsis(diffStats{})
				break
			}
			 := .Field()
			if supportExporters && !isExported(.Name) {
				 = retrieveUnexportedField(, , true)
			}
			 := .WithTypeMode(autoType).(, .Kind(), )
			 = append(, textRecord{Key: .Name, Value: })
		}
		return &textWrap{Prefix: "{", Value: , Suffix: "}"}
	case reflect.Slice:
		if .IsNil() {
			return textNil
		}

		// Check whether this is a []byte of text data.
		if .Elem() == byteType {
			 := .Bytes()
			 := func( rune) bool { return unicode.IsPrint() || unicode.IsSpace() }
			if len() > 0 && utf8.Valid() && len(bytes.TrimFunc(, )) == 0 {
				 = .formatString("", string())
				 = true
				return .FormatType(, )
			}
		}

		fallthrough
	case reflect.Array:
		 := .Len()
		if .LimitVerbosity {
			 = ((1 << .verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc...
			.VerbosityLevel--
		}
		var  textList
		for  := 0;  < .Len(); ++ {
			if len() ==  {
				.AppendEllipsis(diffStats{})
				break
			}
			 := .WithTypeMode(elideType).(.Index(), .Kind(), )
			 = append(, textRecord{Value: })
		}

		 = &textWrap{Prefix: "{", Value: , Suffix: "}"}
		if .Kind() == reflect.Slice && .PrintAddresses {
			 := fmt.Sprintf("ptr:%v, len:%d, cap:%d", formatPointer(value.PointerOf(), false), .Len(), .Cap())
			 = &textWrap{Prefix: pointerDelimPrefix +  + pointerDelimSuffix, Value: }
		}
		return 
	case reflect.Map:
		if .IsNil() {
			return textNil
		}

		// Check pointer for cycles.
		,  := .Push()
		if  {
			return makeLeafReference(, .PrintAddresses)
		}
		defer .Pop()

		 := .Len()
		if .LimitVerbosity {
			 = ((1 << .verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc...
			.VerbosityLevel--
		}
		var  textList
		for ,  := range value.SortKeys(.MapKeys()) {
			if len() ==  {
				.AppendEllipsis(diffStats{})
				break
			}
			 := formatMapKey(, false, )
			 := .WithTypeMode(elideType).(.MapIndex(), .Kind(), )
			 = append(, textRecord{Key: , Value: })
		}

		 = &textWrap{Prefix: "{", Value: , Suffix: "}"}
		 = wrapTrunkReference(, .PrintAddresses, )
		return 
	case reflect.Ptr:
		if .IsNil() {
			return textNil
		}

		// Check pointer for cycles.
		,  := .Push()
		if  {
			 = makeLeafReference(, .PrintAddresses)
			return &textWrap{Prefix: "&", Value: }
		}
		defer .Pop()

		// Skip the name only if this is an unnamed pointer type.
		// Otherwise taking the address of a value does not reproduce
		// the named pointer type.
		if .Type().Name() == "" {
			 = true // Let the underlying value print the type instead
		}
		 = .(.Elem(), .Kind(), )
		 = wrapTrunkReference(, .PrintAddresses, )
		 = &textWrap{Prefix: "&", Value: }
		return 
	case reflect.Interface:
		if .IsNil() {
			return textNil
		}
		// Interfaces accept different concrete types,
		// so configure the underlying value to explicitly print the type.
		return .WithTypeMode(emitType).(.Elem(), .Kind(), )
	default:
		panic(fmt.Sprintf("%v kind not handled", .Kind()))
	}
}

func ( formatOptions) (,  string) textNode {
	 := len()
	 := strings.Count(, "\n") + 1
	if .LimitVerbosity {
		 = (1 << .verbosity()) << 5   // 32, 64, 128, 256, etc...
		 = (1 << .verbosity()) << 2 //  4, 8, 16, 32, 64, etc...
	}

	// For multiline strings, use the triple-quote syntax,
	// but only use it when printing removed or inserted nodes since
	// we only want the extra verbosity for those cases.
	 := strings.Split(strings.TrimSuffix(, "\n"), "\n")
	 := len() >= 4 && (.DiffMode == '-' || .DiffMode == '+')
	for  := 0;  < len() && ; ++ {
		[] = strings.TrimPrefix(strings.TrimSuffix([], "\r"), "\r") // trim leading/trailing carriage returns for legacy Windows endline support
		 := func( rune) bool {
			return unicode.IsPrint() ||  == '\t' // specially treat tab as printable
		}
		 := []
		 = !strings.HasPrefix(strings.TrimPrefix(, ), `"""`) && !strings.HasPrefix(, "...") && strings.TrimFunc(, ) == "" && len() <= 
	}
	if  {
		var  textList
		 = append(, textRecord{Diff: .DiffMode, Value: textLine( + `"""`), ElideComma: true})
		for ,  := range  {
			if  := len() - ;  == -1 &&  > 1 {
				 := commentString(fmt.Sprintf("%d elided lines", ))
				 = append(, textRecord{Diff: .DiffMode, Value: textEllipsis, ElideComma: true, Comment: })
				break
			}
			 = append(, textRecord{Diff: .DiffMode, Value: textLine(), ElideComma: true})
		}
		 = append(, textRecord{Diff: .DiffMode, Value: textLine( + `"""`), ElideComma: true})
		return &textWrap{Prefix: "(", Value: , Suffix: ")"}
	}

	// Format the string as a single-line quoted string.
	if len() > +len(textEllipsis) {
		return textLine( + formatString([:]) + string(textEllipsis))
	}
	return textLine( + formatString())
}

// formatMapKey formats v as if it were a map key.
// The result is guaranteed to be a single line.
func ( reflect.Value,  bool,  *pointerReferences) string {
	var  formatOptions
	.DiffMode = diffIdentical
	.TypeMode = elideType
	.PrintAddresses = 
	.AvoidStringer = 
	.QualifiedNames = 
	.VerbosityLevel = maxVerbosityPreset
	.LimitVerbosity = true
	 := .FormatValue(, reflect.Map, ).String()
	return strings.TrimSpace()
}

// formatString prints s as a double-quoted or backtick-quoted string.
func ( string) string {
	// Use quoted string if it the same length as a raw string literal.
	// Otherwise, attempt to use the raw string form.
	 := strconv.Quote()
	if len() == 1+len()+1 {
		return 
	}

	// Disallow newlines to ensure output is a single line.
	// Only allow printable runes for readability purposes.
	 := func( rune) bool {
		return  == '`' ||  == '\n' || !(unicode.IsPrint() ||  == '\t')
	}
	if utf8.ValidString() && strings.IndexFunc(, ) < 0 {
		return "`" +  + "`"
	}
	return 
}

// formatHex prints u as a hexadecimal integer in Go notation.
func ( uint64) string {
	var  string
	switch {
	case  <= 0xff:
		 = "0x%02x"
	case  <= 0xffff:
		 = "0x%04x"
	case  <= 0xffffff:
		 = "0x%06x"
	case  <= 0xffffffff:
		 = "0x%08x"
	case  <= 0xffffffffff:
		 = "0x%010x"
	case  <= 0xffffffffffff:
		 = "0x%012x"
	case  <= 0xffffffffffffff:
		 = "0x%014x"
	case  <= 0xffffffffffffffff:
		 = "0x%016x"
	}
	return fmt.Sprintf(, )
}