package zapjournal

import (
	
	
	
	
	
	

	
)

// hdrLen is the size of the length header for a multi-line variable. The header
// is a single 64-bit little endian integer.
const hdrLen = 8

// Severity levels used in PRIORITY field for syslog compatibility.
const (
	priEmerg = string('0' + iota)
	priAlert
	priCrit
	priErr
	priWarning
	priNotice
	priInfo
	priDebug
)

// varsEncoder is a partial zapcore.Encoder implementation that encodes entries
// to the systemd-journald wire format. Top-level fields are set as variables
// with the configured prefix with open namespaces appended, while composite
// values (arrays and objects) and reflected values are encoded as JSON.
//
// Note that, in JSON mode, numeric values (float, complex, int) are encoded as
// strings. This addresses the limitations of JSON encoding, i.e. the lack of
// complex numbers, NaN and Inf, and integer interoperability. This does not
// apply to the reflected values though, that use standard library for encoding.
type varsEncoder struct {
	// prefix is appended to variable names.
	prefix string

	// buf holds the encoded variables.
	buf []byte

	// hdr, if non zero, is an index to buf that is used in endVar to write
	// the length of the data appended since beginVar.
	hdr int

	// json encoder is used to encode composite values (objects and arrays).
	json jsonEncoder
}

// beginSinglelineVar starts a single-line variable.
func ( *varsEncoder) ( string) {
	.beginVar(, false)
}

// beginMultilineVar starts a multi-line variable.
func ( *varsEncoder) ( string) {
	.beginVar(, true)
}

// beginVar starts a single-line or multi-line variable. It should not be used
// directly. Instead, call the convenience functions beginSinglelineVar and
// beginMultilineVar. Each call should have a corresponding endVar. Nested
// variables are not allowed.
func ( *varsEncoder) ( string,  bool) {
	.buf = appendVarName(.buf, .prefix, )
	if ! {
		.buf = append(.buf, '=')
		return
	}
	.buf = append(.buf, '\n')
	.hdr = len(.buf)
	.buf = append(.buf, make([]byte, hdrLen)...)
}

// endVar ends a variable. Both single-line and multi-line variables are
// supported.
func ( *varsEncoder) () {
	if .hdr != 0 {
		 := uint64(len(.buf) - .hdr - hdrLen)
		binary.LittleEndian.PutUint64(.buf[.hdr:], )
		.hdr = 0
	}
	.buf = append(.buf, '\n')
}

func ( *varsEncoder) ( string) {
	.prefix += "_" + 
}

func ( *varsEncoder) ( string,  []byte) {
	.beginMultilineVar()
	.buf = append(.buf, ...)
	.endVar()
}

func ( *varsEncoder) ( string,  []byte) {
	 := bytes.Contains(, []byte{'\n'})
	.beginVar(, )
	.buf = append(.buf, ...)
	.endVar()
}

func ( *varsEncoder) (,  string) {
	 := strings.Contains(, "\n")
	.beginVar(, )
	.buf = append(.buf, ...)
	.endVar()
}

func ( *varsEncoder) ( string,  bool) {
	.beginSinglelineVar()
	.buf = strconv.AppendBool(.buf, )
	.endVar()
}

func ( *varsEncoder) ( string,  complex128) {
	.addComplex(, , 128)
}

func ( *varsEncoder) ( string,  complex64) {
	.addComplex(, complex128(), 64)
}

func ( *varsEncoder) ( string,  complex128,  int) {
	.beginSinglelineVar()
	.buf = strconvAppendComplex(.buf, , 'g', -1, )
	.endVar()
}

func ( *varsEncoder) ( string,  time.Duration) {
	.AddString(, .String())
}

func ( *varsEncoder) ( string,  float64) {
	.addFloat(, , 64)
}

func ( *varsEncoder) ( string,  float32) {
	.addFloat(, float64(), 32)
}

func ( *varsEncoder) ( string,  float64,  int) {
	.beginSinglelineVar()
	.buf = strconv.AppendFloat(.buf, , 'g', -1, )
	.endVar()
}

func ( *varsEncoder) ( string,  int) {
	.AddInt64(, int64())
}

func ( *varsEncoder) ( string,  int64) {
	.beginSinglelineVar()
	.buf = strconv.AppendInt(.buf, , 10)
	.endVar()
}

func ( *varsEncoder) ( string,  int32) {
	.AddInt64(, int64())
}

func ( *varsEncoder) ( string,  int16) {
	.AddInt64(, int64())
}

func ( *varsEncoder) ( string,  int8) {
	.AddInt64(, int64())
}

func ( *varsEncoder) ( string,  uint) {
	.AddUint64(, uint64())
}

func ( *varsEncoder) ( string,  uint64) {
	.beginSinglelineVar()
	.buf = strconv.AppendUint(.buf, , 10)
	.endVar()
}

func ( *varsEncoder) ( string,  uint32) {
	.AddUint64(, uint64())
}

func ( *varsEncoder) ( string,  uint16) {
	.AddUint64(, uint64())
}

func ( *varsEncoder) ( string,  uint8) {
	.AddUint64(, uint64())
}

func ( *varsEncoder) ( string,  uintptr) {
	.beginSinglelineVar()
	.buf = strconvAppendUintptr(.buf, )
	.endVar()
}

func ( *varsEncoder) ( string,  time.Time) {
	.beginSinglelineVar()
	.buf = .AppendFormat(.buf, time.RFC3339Nano)
	.endVar()
}

func ( *varsEncoder) ( string,  any) error {
	.beginMultilineVar()
	 := bytes.NewBuffer(.buf)
	 := json.NewEncoder()
	.SetEscapeHTML(false)
	 := .Encode()
	.buf = .Bytes()
	// Remove newline character that is always added by Encode.
	// See https://stackoverflow.com/a/36320146
	.buf = .buf[:len(.buf)-1]
	.endVar()
	return 
}

func ( *varsEncoder) ( string,  zapcore.ArrayMarshaler) error {
	.beginMultilineVar()
	.json.appendOpenArray()
	 := .MarshalLogArray(&.json)
	.json.complete()
	.json.appendCloseArray()
	.endVar()
	return 
}

func ( *varsEncoder) ( string,  zapcore.ObjectMarshaler) error {
	.beginMultilineVar()
	.json.appendOpenObject()
	 := .MarshalLogObject(&.json)
	.json.complete()
	.json.appendCloseObject()
	.endVar()
	return 
}

func ( *varsEncoder) ( zapcore.Entry) {
	// Temporarily unset prefix to add entry variables.
	 := .prefix
	.prefix = ""

	// https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html#MESSAGE=
	.AddString("MESSAGE", .Message)

	// https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html#CODE_FILE=
	if  := .Caller; .Defined {
		.AddString("CODE_FILE", .File)
		.AddInt("CODE_LINE", .Line)
		.AddString("CODE_FUNC", .Function)
	}

	// https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html#PRIORITY=
	 := priNotice
	switch .Level {
	case zapcore.DebugLevel:
		 = priDebug
	case zapcore.InfoLevel:
		 = priInfo
	case zapcore.WarnLevel:
		 = priWarning
	case zapcore.ErrorLevel:
		 = priErr
	case zapcore.DPanicLevel:
		 = priCrit
	case zapcore.PanicLevel:
		 = priAlert
	case zapcore.FatalLevel:
		 = priEmerg
	}
	.AddString("PRIORITY", )

	// Also add textual representation of the log level.
	.AddString("LOG_LEVEL", .Level.String())

	if !.Time.IsZero() {
		.AddTime("TIMESTAMP", .Time)
	}

	if .LoggerName != "" {
		.AddString("LOG_NAME", .LoggerName)
	}

	if .Stack != "" {
		.AddString("STACK", .Stack)
	}

	.prefix = 
}

// encodeEntry encodes the entry using the underlying buffer as the initial
// state. Note that it copies the internal buffer and does not mutate it. The
// resulting encoder is owned by the caller.
func ( *varsEncoder) ( zapcore.Entry,  []zapcore.Field) *varsEncoder {
	 = cloneVarsEncoder()
	addFields(, )
	.encodeEntryVars()
	return 
}

func ( zapcore.ObjectEncoder,  []zapcore.Field) {
	for  := range  {
		[].AddTo()
	}
}