// Copyright 2017 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 catmsg

import (
	
	

	
)

// A Renderer renders a Message.
type Renderer interface {
	// Render renders the given string. The given string may be interpreted as a
	// format string, such as the one used by the fmt package or a template.
	Render(s string)

	// Arg returns the i-th argument passed to format a message. This method
	// should return nil if there is no such argument. Messages need access to
	// arguments to allow selecting a message based on linguistic features of
	// those arguments.
	Arg(i int) interface{}
}

// A Dictionary specifies a source of messages, including variables or macros.
type Dictionary interface {
	// Lookup returns the message for the given key. It returns false for ok if
	// such a message could not be found.
	Lookup(key string) (data string, ok bool)

	// TODO: consider returning an interface, instead of a string. This will
	// allow implementations to do their own message type decoding.
}

// An Encoder serializes a Message to a string.
type Encoder struct {
	// The root encoder is used for storing encoded variables.
	root *Encoder
	// The parent encoder provides the surrounding scopes for resolving variable
	// names.
	parent *Encoder

	tag language.Tag

	// buf holds the encoded message so far. After a message completes encoding,
	// the contents of buf, prefixed by the encoded length, are flushed to the
	// parent buffer.
	buf []byte

	// vars is the lookup table of variables in the current scope.
	vars []keyVal

	err    error
	inBody bool // if false next call must be EncodeMessageType
}

type keyVal struct {
	key    string
	offset int
}

// Language reports the language for which the encoded message will be stored
// in the Catalog.
func ( *Encoder) () language.Tag { return .tag }

func ( *Encoder) ( error) {
	if .root.err == nil {
		.root.err = 
	}
}

// EncodeUint encodes x.
func ( *Encoder) ( uint64) {
	.checkInBody()
	var  [maxVarintBytes]byte
	 := encodeUint([:], )
	.buf = append(.buf, [:]...)
}

// EncodeString encodes s.
func ( *Encoder) ( string) {
	.checkInBody()
	.EncodeUint(uint64(len()))
	.buf = append(.buf, ...)
}

// EncodeMessageType marks the current message to be of type h.
//
// It must be the first call of a Message's Compile method.
func ( *Encoder) ( Handle) {
	if .inBody {
		panic("catmsg: EncodeMessageType not the first method called")
	}
	.inBody = true
	.EncodeUint(uint64())
}

// EncodeMessage serializes the given message inline at the current position.
func ( *Encoder) ( Message) error {
	 = &Encoder{root: .root, parent: , tag: .tag}
	 := .Compile()
	if ,  := .(*Var); ! {
		.flushTo(.parent)
	}
	return 
}

func ( *Encoder) () {
	if !.inBody {
		panic("catmsg: expected prior call to EncodeMessageType")
	}
}

// stripPrefix indicates the number of prefix bytes that must be stripped to
// turn a single-element sequence into a message that is just this single member
// without its size prefix. If the message can be stripped, b[1:n] contains the
// size prefix.
func stripPrefix( []byte) ( int) {
	if len() > 0 && Handle([0]) == msgFirst {
		, ,  := decodeUint([1:])
		if 1++int() == len() {
			return 1 + 
		}
	}
	return 0
}

func ( *Encoder) ( *Encoder) {
	 := .buf
	 := stripPrefix()
	if  > 0 {
		 = [1:]
	} else {
		// Prefix the size.
		.EncodeUint(uint64(len()))
	}
	.buf = append(.buf, ...)
}

func ( *Encoder) ( string,  Message) error {
	for ,  := range .parent.vars {
		if .key ==  {
			 := fmt.Errorf("catmsg: duplicate variable %q", )
			.setError()
			return 
		}
	}
	 := .parent
	// If a variable message is Incomplete, and does not evaluate to a message
	// during execution, we fall back to the variable name. We encode this by
	// appending the variable name if the message reports it's incomplete.

	 := .Compile()
	if  != ErrIncomplete {
		.setError()
	}
	switch {
	case len(.buf) == 1 && Handle(.buf[0]) == msgFirst: // empty sequence
		.buf = .buf[:0]
		.inBody = false
		fallthrough
	case len(.buf) == 0:
		// Empty message.
		if  := String().Compile();  != nil {
			.setError()
		}
	case  == ErrIncomplete:
		if Handle(.buf[0]) != msgFirst {
			 := &Encoder{root: .root, parent: }
			.EncodeMessageType(msgFirst)
			.flushTo()
			 = 
		}
		// e contains a sequence; append the fallback string.
		.EncodeMessage(String())
	}

	// Flush result to variable heap.
	 := len(.root.buf)
	.flushTo(.root)
	.buf = .buf[:0]

	// Record variable offset in current scope.
	.vars = append(.vars, keyVal{key: , offset: })
	return 
}

const (
	substituteVar = iota
	substituteMacro
	substituteError
)

// EncodeSubstitution inserts a resolved reference to a variable or macro.
//
// This call must be matched with a call to ExecuteSubstitution at decoding
// time.
func ( *Encoder) ( string,  ...int) {
	if  := len();  > 0 {
		// TODO: also resolve macros.
		.EncodeUint(substituteMacro)
		.EncodeString()
		for ,  := range  {
			.EncodeUint(uint64())
		}
		return
	}
	for  := ;  != nil;  = .parent {
		for ,  := range .vars {
			if .key !=  {
				continue
			}
			.EncodeUint(substituteVar) // TODO: support arity > 0
			.EncodeUint(uint64(.offset))
			return
		}
	}
	// TODO: refer to dictionary-wide scoped variables.
	.EncodeUint(substituteError)
	.EncodeString()
	.setError(fmt.Errorf("catmsg: unknown var %q", ))
}

// A Decoder deserializes and evaluates messages that are encoded by an encoder.
type Decoder struct {
	tag    language.Tag
	dst    Renderer
	macros Dictionary

	err  error
	vars string
	data string

	macroArg int // TODO: allow more than one argument
}

// NewDecoder returns a new Decoder.
//
// Decoders are designed to be reused for multiple invocations of Execute.
// Only one goroutine may call Execute concurrently.
func ( language.Tag,  Renderer,  Dictionary) *Decoder {
	return &Decoder{
		tag:    ,
		dst:    ,
		macros: ,
	}
}

func ( *Decoder) ( error) {
	if .err == nil {
		.err = 
	}
}

// Language returns the language in which the message is being rendered.
//
// The destination language may be a child language of the language used for
// encoding. For instance, a decoding language of "pt-PT" is consistent with an
// encoding language of "pt".
func ( *Decoder) () language.Tag { return .tag }

// Done reports whether there are more bytes to process in this message.
func ( *Decoder) () bool { return len(.data) == 0 }

// Render implements Renderer.
func ( *Decoder) ( string) { .dst.Render() }

// Arg implements Renderer.
//
// During evaluation of macros, the argument positions may be mapped to
// arguments that differ from the original call.
func ( *Decoder) ( int) interface{} {
	if .macroArg != 0 {
		if  != 1 {
			panic("catmsg: only macros with single argument supported")
		}
		 = .macroArg
	}
	return .dst.Arg()
}

// DecodeUint decodes a number that was encoded with EncodeUint and advances the
// position.
func ( *Decoder) () uint64 {
	, ,  := decodeUintString(.data)
	.data = .data[:]
	if  != nil {
		.setError()
	}
	return 
}

// DecodeString decodes a string that was encoded with EncodeString and advances
// the position.
func ( *Decoder) () string {
	 := .DecodeUint()
	 := .data[:]
	.data = .data[:]
	return 
}

// SkipMessage skips the message at the current location and advances the
// position.
func ( *Decoder) () {
	 := int(.DecodeUint())
	.data = .data[:]
}

// Execute decodes and evaluates msg.
//
// Only one goroutine may call execute.
func ( *Decoder) ( string) error {
	.err = nil
	if !.execute() {
		return ErrNoMatch
	}
	return .err
}

func ( *Decoder) ( string) bool {
	 := .data
	.data = 
	 := .executeMessage()
	.data = 
	return 
}

// executeMessageFromData is like execute, but also decodes a leading message
// size and clips the given string accordingly.
//
// It reports the number of bytes consumed and whether a message was selected.
func ( *Decoder) ( string) ( int,  bool) {
	 := .data
	.data = 
	 := int(.DecodeUint())
	 = len() - len(.data)
	// Sanitize the setting. This allows skipping a size argument for
	// RawString and method Done.
	.data = .data[:]
	 = .executeMessage()
	 +=  - len(.data)
	.data = 
	return , 
}

var errUnknownHandler = errors.New("catmsg: string contains unsupported handler")

// executeMessage reads the handle id, initializes the decoder and executes the
// message. It is assumed that all of d.data[d.p:] is the single message.
func ( *Decoder) () bool {
	if .Done() {
		// We interpret no data as a valid empty message.
		return true
	}
	 := .DecodeUint()

	var  Handler
	mutex.Lock()
	if int() < len(handlers) {
		 = handlers[]
	}
	mutex.Unlock()
	if  == nil {
		.setError(errUnknownHandler)
		.execute(fmt.Sprintf("\x02$!(UNKNOWNMSGHANDLER=%#x)", ))
		return true
	}
	return ()
}

// ExecuteMessage decodes and executes the message at the current position.
func ( *Decoder) () bool {
	,  := .executeMessageFromData(.data)
	.data = .data[:]
	return 
}

// ExecuteSubstitution executes the message corresponding to the substitution
// as encoded by EncodeSubstitution.
func ( *Decoder) () {
	switch  := .DecodeUint();  {
	case substituteVar:
		 := .DecodeUint()
		.executeMessageFromData(.vars[:])
	case substituteMacro:
		 := .DecodeString()
		,  := .macros.Lookup()
		 := .macroArg
		// TODO: support macros of arity other than 1.
		.macroArg = int(.DecodeUint())
		switch {
		case !:
			// TODO: detect this at creation time.
			.setError(fmt.Errorf("catmsg: undefined macro %q", ))
			fallthrough
		case !.execute():
			.dst.Render() // fall back to macro name.
		}
		.macroArg = 
	case substituteError:
		.dst.Render(.DecodeString())
	default:
		panic("catmsg: unreachable")
	}
}