// Copyright 2018 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 text

import (
	
	
	
	
	

	
	
)

// encType represents an encoding type.
type encType uint8

const (
	_ encType = (1 << iota) / 2
	name
	scalar
	messageOpen
	messageClose
)

// Encoder provides methods to write out textproto constructs and values. The user is
// responsible for producing valid sequences of constructs and values.
type Encoder struct {
	encoderState

	indent      string
	delims      [2]byte
	outputASCII bool
}

type encoderState struct {
	lastType encType
	indents  []byte
	out      []byte
}

// NewEncoder returns an Encoder.
//
// If indent is a non-empty string, it causes every entry in a List or Message
// to be preceded by the indent and trailed by a newline.
//
// If delims is not the zero value, it controls the delimiter characters used
// for messages (e.g., "{}" vs "<>").
//
// If outputASCII is true, strings will be serialized in such a way that
// multi-byte UTF-8 sequences are escaped. This property ensures that the
// overall output is ASCII (as opposed to UTF-8).
func ( string,  [2]byte,  bool) (*Encoder, error) {
	 := &Encoder{}
	if len() > 0 {
		if strings.Trim(, " \t") != "" {
			return nil, errors.New("indent may only be composed of space and tab characters")
		}
		.indent = 
	}
	switch  {
	case [2]byte{0, 0}:
		.delims = [2]byte{'{', '}'}
	case [2]byte{'{', '}'}, [2]byte{'<', '>'}:
		.delims = 
	default:
		return nil, errors.New("delimiters may only be \"{}\" or \"<>\"")
	}
	.outputASCII = 

	return , nil
}

// Bytes returns the content of the written bytes.
func ( *Encoder) () []byte {
	return .out
}

// StartMessage writes out the '{' or '<' symbol.
func ( *Encoder) () {
	.prepareNext(messageOpen)
	.out = append(.out, .delims[0])
}

// EndMessage writes out the '}' or '>' symbol.
func ( *Encoder) () {
	.prepareNext(messageClose)
	.out = append(.out, .delims[1])
}

// WriteName writes out the field name and the separator ':'.
func ( *Encoder) ( string) {
	.prepareNext(name)
	.out = append(.out, ...)
	.out = append(.out, ':')
}

// WriteBool writes out the given boolean value.
func ( *Encoder) ( bool) {
	if  {
		.WriteLiteral("true")
	} else {
		.WriteLiteral("false")
	}
}

// WriteString writes out the given string value.
func ( *Encoder) ( string) {
	.prepareNext(scalar)
	.out = appendString(.out, , .outputASCII)
}

func ( []byte,  string,  bool) []byte {
	 = append(, '"')
	 := indexNeedEscapeInString()
	,  = [:], append(, [:]...)
	for len() > 0 {
		switch ,  := utf8.DecodeRuneInString(); {
		case  == utf8.RuneError &&  == 1:
			// We do not report invalid UTF-8 because strings in the text format
			// are used to represent both the proto string and bytes type.
			 = rune([0])
			fallthrough
		case  < ' ' ||  == '"' ||  == '\\' ||  == 0x7f:
			 = append(, '\\')
			switch  {
			case '"', '\\':
				 = append(, byte())
			case '\n':
				 = append(, 'n')
			case '\r':
				 = append(, 'r')
			case '\t':
				 = append(, 't')
			default:
				 = append(, 'x')
				 = append(, "00"[1+(bits.Len32(uint32())-1)/4:]...)
				 = strconv.AppendUint(, uint64(), 16)
			}
			 = [:]
		case  >= utf8.RuneSelf && ( ||  <= 0x009f):
			 = append(, '\\')
			if  <= math.MaxUint16 {
				 = append(, 'u')
				 = append(, "0000"[1+(bits.Len32(uint32())-1)/4:]...)
				 = strconv.AppendUint(, uint64(), 16)
			} else {
				 = append(, 'U')
				 = append(, "00000000"[1+(bits.Len32(uint32())-1)/4:]...)
				 = strconv.AppendUint(, uint64(), 16)
			}
			 = [:]
		default:
			 := indexNeedEscapeInString([:])
			,  = [+:], append(, [:+]...)
		}
	}
	 = append(, '"')
	return 
}

// indexNeedEscapeInString returns the index of the character that needs
// escaping. If no characters need escaping, this returns the input length.
func ( string) int {
	for  := 0;  < len(); ++ {
		if  := [];  < ' ' ||  == '"' ||  == '\'' ||  == '\\' ||  >= 0x7f {
			return 
		}
	}
	return len()
}

// WriteFloat writes out the given float value for given bitSize.
func ( *Encoder) ( float64,  int) {
	.prepareNext(scalar)
	.out = appendFloat(.out, , )
}

func ( []byte,  float64,  int) []byte {
	switch {
	case math.IsNaN():
		return append(, "nan"...)
	case math.IsInf(, +1):
		return append(, "inf"...)
	case math.IsInf(, -1):
		return append(, "-inf"...)
	default:
		return strconv.AppendFloat(, , 'g', -1, )
	}
}

// WriteInt writes out the given signed integer value.
func ( *Encoder) ( int64) {
	.prepareNext(scalar)
	.out = append(.out, strconv.FormatInt(, 10)...)
}

// WriteUint writes out the given unsigned integer value.
func ( *Encoder) ( uint64) {
	.prepareNext(scalar)
	.out = append(.out, strconv.FormatUint(, 10)...)
}

// WriteLiteral writes out the given string as a literal value without quotes.
// This is used for writing enum literal strings.
func ( *Encoder) ( string) {
	.prepareNext(scalar)
	.out = append(.out, ...)
}

// prepareNext adds possible space and indentation for the next value based
// on last encType and indent option. It also updates e.lastType to next.
func ( *Encoder) ( encType) {
	defer func() {
		.lastType = 
	}()

	// Single line.
	if len(.indent) == 0 {
		// Add space after each field before the next one.
		if .lastType&(scalar|messageClose) != 0 &&  == name {
			.out = append(.out, ' ')
			// Add a random extra space to make output unstable.
			if detrand.Bool() {
				.out = append(.out, ' ')
			}
		}
		return
	}

	// Multi-line.
	switch {
	case .lastType == name:
		.out = append(.out, ' ')
		// Add a random extra space after name: to make output unstable.
		if detrand.Bool() {
			.out = append(.out, ' ')
		}

	case .lastType == messageOpen &&  != messageClose:
		.indents = append(.indents, .indent...)
		.out = append(.out, '\n')
		.out = append(.out, .indents...)

	case .lastType&(scalar|messageClose) != 0:
		if  == messageClose {
			.indents = .indents[:len(.indents)-len(.indent)]
		}
		.out = append(.out, '\n')
		.out = append(.out, .indents...)
	}
}

// Snapshot returns the current snapshot for use in Reset.
func ( *Encoder) () encoderState {
	return .encoderState
}

// Reset resets the Encoder to the given encoderState from a Snapshot.
func ( *Encoder) ( encoderState) {
	.encoderState = 
}

// AppendString appends the escaped form of the input string to b.
func ( []byte,  string) []byte {
	return appendString(, , false)
}