package pgproto3

import (
	
	
	
	
	
	
	
)

// tracer traces the messages send to and from a Backend or Frontend. The format it produces roughly mimics the
// format produced by the libpq C function PQtrace.
type tracer struct {
	TracerOptions

	mux sync.Mutex
	w   io.Writer
	buf *bytes.Buffer
}

// TracerOptions controls tracing behavior. It is roughly equivalent to the libpq function PQsetTraceFlags.
type TracerOptions struct {
	// SuppressTimestamps prevents printing of timestamps.
	SuppressTimestamps bool

	// RegressMode redacts fields that may be vary between executions.
	RegressMode bool
}

func ( *tracer) ( byte,  int32,  Message) {
	switch msg := .(type) {
	case *AuthenticationCleartextPassword:
		.traceAuthenticationCleartextPassword(, , )
	case *AuthenticationGSS:
		.traceAuthenticationGSS(, , )
	case *AuthenticationGSSContinue:
		.traceAuthenticationGSSContinue(, , )
	case *AuthenticationMD5Password:
		.traceAuthenticationMD5Password(, , )
	case *AuthenticationOk:
		.traceAuthenticationOk(, , )
	case *AuthenticationSASL:
		.traceAuthenticationSASL(, , )
	case *AuthenticationSASLContinue:
		.traceAuthenticationSASLContinue(, , )
	case *AuthenticationSASLFinal:
		.traceAuthenticationSASLFinal(, , )
	case *BackendKeyData:
		.traceBackendKeyData(, , )
	case *Bind:
		.traceBind(, , )
	case *BindComplete:
		.traceBindComplete(, , )
	case *CancelRequest:
		.traceCancelRequest(, , )
	case *Close:
		.traceClose(, , )
	case *CloseComplete:
		.traceCloseComplete(, , )
	case *CommandComplete:
		.traceCommandComplete(, , )
	case *CopyBothResponse:
		.traceCopyBothResponse(, , )
	case *CopyData:
		.traceCopyData(, , )
	case *CopyDone:
		.traceCopyDone(, , )
	case *CopyFail:
		.traceCopyFail(, , )
	case *CopyInResponse:
		.traceCopyInResponse(, , )
	case *CopyOutResponse:
		.traceCopyOutResponse(, , )
	case *DataRow:
		.traceDataRow(, , )
	case *Describe:
		.traceDescribe(, , )
	case *EmptyQueryResponse:
		.traceEmptyQueryResponse(, , )
	case *ErrorResponse:
		.traceErrorResponse(, , )
	case *Execute:
		.TraceQueryute(, , )
	case *Flush:
		.traceFlush(, , )
	case *FunctionCall:
		.traceFunctionCall(, , )
	case *FunctionCallResponse:
		.traceFunctionCallResponse(, , )
	case *GSSEncRequest:
		.traceGSSEncRequest(, , )
	case *NoData:
		.traceNoData(, , )
	case *NoticeResponse:
		.traceNoticeResponse(, , )
	case *NotificationResponse:
		.traceNotificationResponse(, , )
	case *ParameterDescription:
		.traceParameterDescription(, , )
	case *ParameterStatus:
		.traceParameterStatus(, , )
	case *Parse:
		.traceParse(, , )
	case *ParseComplete:
		.traceParseComplete(, , )
	case *PortalSuspended:
		.tracePortalSuspended(, , )
	case *Query:
		.traceQuery(, , )
	case *ReadyForQuery:
		.traceReadyForQuery(, , )
	case *RowDescription:
		.traceRowDescription(, , )
	case *SSLRequest:
		.traceSSLRequest(, , )
	case *StartupMessage:
		.traceStartupMessage(, , )
	case *Sync:
		.traceSync(, , )
	case *Terminate:
		.traceTerminate(, , )
	default:
		.writeTrace(, , "Unknown", nil)
	}
}

func ( *tracer) ( byte,  int32,  *AuthenticationCleartextPassword) {
	.writeTrace(, , "AuthenticationCleartextPassword", nil)
}

func ( *tracer) ( byte,  int32,  *AuthenticationGSS) {
	.writeTrace(, , "AuthenticationGSS", nil)
}

func ( *tracer) ( byte,  int32,  *AuthenticationGSSContinue) {
	.writeTrace(, , "AuthenticationGSSContinue", nil)
}

func ( *tracer) ( byte,  int32,  *AuthenticationMD5Password) {
	.writeTrace(, , "AuthenticationMD5Password", nil)
}

func ( *tracer) ( byte,  int32,  *AuthenticationOk) {
	.writeTrace(, , "AuthenticationOk", nil)
}

func ( *tracer) ( byte,  int32,  *AuthenticationSASL) {
	.writeTrace(, , "AuthenticationSASL", nil)
}

func ( *tracer) ( byte,  int32,  *AuthenticationSASLContinue) {
	.writeTrace(, , "AuthenticationSASLContinue", nil)
}

func ( *tracer) ( byte,  int32,  *AuthenticationSASLFinal) {
	.writeTrace(, , "AuthenticationSASLFinal", nil)
}

func ( *tracer) ( byte,  int32,  *BackendKeyData) {
	.writeTrace(, , "BackendKeyData", func() {
		if .RegressMode {
			.buf.WriteString("\t NNNN NNNN")
		} else {
			fmt.Fprintf(.buf, "\t %d %d", .ProcessID, .SecretKey)
		}
	})
}

func ( *tracer) ( byte,  int32,  *Bind) {
	.writeTrace(, , "Bind", func() {
		fmt.Fprintf(.buf, "\t %s %s %d", traceDoubleQuotedString([]byte(.DestinationPortal)), traceDoubleQuotedString([]byte(.PreparedStatement)), len(.ParameterFormatCodes))
		for ,  := range .ParameterFormatCodes {
			fmt.Fprintf(.buf, " %d", )
		}
		fmt.Fprintf(.buf, " %d", len(.Parameters))
		for ,  := range .Parameters {
			fmt.Fprintf(.buf, " %s", traceSingleQuotedString())
		}
		fmt.Fprintf(.buf, " %d", len(.ResultFormatCodes))
		for ,  := range .ResultFormatCodes {
			fmt.Fprintf(.buf, " %d", )
		}
	})
}

func ( *tracer) ( byte,  int32,  *BindComplete) {
	.writeTrace(, , "BindComplete", nil)
}

func ( *tracer) ( byte,  int32,  *CancelRequest) {
	.writeTrace(, , "CancelRequest", nil)
}

func ( *tracer) ( byte,  int32,  *Close) {
	.writeTrace(, , "Close", nil)
}

func ( *tracer) ( byte,  int32,  *CloseComplete) {
	.writeTrace(, , "CloseComplete", nil)
}

func ( *tracer) ( byte,  int32,  *CommandComplete) {
	.writeTrace(, , "CommandComplete", func() {
		fmt.Fprintf(.buf, "\t %s", traceDoubleQuotedString(.CommandTag))
	})
}

func ( *tracer) ( byte,  int32,  *CopyBothResponse) {
	.writeTrace(, , "CopyBothResponse", nil)
}

func ( *tracer) ( byte,  int32,  *CopyData) {
	.writeTrace(, , "CopyData", nil)
}

func ( *tracer) ( byte,  int32,  *CopyDone) {
	.writeTrace(, , "CopyDone", nil)
}

func ( *tracer) ( byte,  int32,  *CopyFail) {
	.writeTrace(, , "CopyFail", func() {
		fmt.Fprintf(.buf, "\t %s", traceDoubleQuotedString([]byte(.Message)))
	})
}

func ( *tracer) ( byte,  int32,  *CopyInResponse) {
	.writeTrace(, , "CopyInResponse", nil)
}

func ( *tracer) ( byte,  int32,  *CopyOutResponse) {
	.writeTrace(, , "CopyOutResponse", nil)
}

func ( *tracer) ( byte,  int32,  *DataRow) {
	.writeTrace(, , "DataRow", func() {
		fmt.Fprintf(.buf, "\t %d", len(.Values))
		for ,  := range .Values {
			if  == nil {
				.buf.WriteString(" -1")
			} else {
				fmt.Fprintf(.buf, " %d %s", len(), traceSingleQuotedString())
			}
		}
	})
}

func ( *tracer) ( byte,  int32,  *Describe) {
	.writeTrace(, , "Describe", func() {
		fmt.Fprintf(.buf, "\t %c %s", .ObjectType, traceDoubleQuotedString([]byte(.Name)))
	})
}

func ( *tracer) ( byte,  int32,  *EmptyQueryResponse) {
	.writeTrace(, , "EmptyQueryResponse", nil)
}

func ( *tracer) ( byte,  int32,  *ErrorResponse) {
	.writeTrace(, , "ErrorResponse", nil)
}

func ( *tracer) ( byte,  int32,  *Execute) {
	.writeTrace(, , "Execute", func() {
		fmt.Fprintf(.buf, "\t %s %d", traceDoubleQuotedString([]byte(.Portal)), .MaxRows)
	})
}

func ( *tracer) ( byte,  int32,  *Flush) {
	.writeTrace(, , "Flush", nil)
}

func ( *tracer) ( byte,  int32,  *FunctionCall) {
	.writeTrace(, , "FunctionCall", nil)
}

func ( *tracer) ( byte,  int32,  *FunctionCallResponse) {
	.writeTrace(, , "FunctionCallResponse", nil)
}

func ( *tracer) ( byte,  int32,  *GSSEncRequest) {
	.writeTrace(, , "GSSEncRequest", nil)
}

func ( *tracer) ( byte,  int32,  *NoData) {
	.writeTrace(, , "NoData", nil)
}

func ( *tracer) ( byte,  int32,  *NoticeResponse) {
	.writeTrace(, , "NoticeResponse", nil)
}

func ( *tracer) ( byte,  int32,  *NotificationResponse) {
	.writeTrace(, , "NotificationResponse", func() {
		fmt.Fprintf(.buf, "\t %d %s %s", .PID, traceDoubleQuotedString([]byte(.Channel)), traceDoubleQuotedString([]byte(.Payload)))
	})
}

func ( *tracer) ( byte,  int32,  *ParameterDescription) {
	.writeTrace(, , "ParameterDescription", nil)
}

func ( *tracer) ( byte,  int32,  *ParameterStatus) {
	.writeTrace(, , "ParameterStatus", func() {
		fmt.Fprintf(.buf, "\t %s %s", traceDoubleQuotedString([]byte(.Name)), traceDoubleQuotedString([]byte(.Value)))
	})
}

func ( *tracer) ( byte,  int32,  *Parse) {
	.writeTrace(, , "Parse", func() {
		fmt.Fprintf(.buf, "\t %s %s %d", traceDoubleQuotedString([]byte(.Name)), traceDoubleQuotedString([]byte(.Query)), len(.ParameterOIDs))
		for ,  := range .ParameterOIDs {
			fmt.Fprintf(.buf, " %d", )
		}
	})
}

func ( *tracer) ( byte,  int32,  *ParseComplete) {
	.writeTrace(, , "ParseComplete", nil)
}

func ( *tracer) ( byte,  int32,  *PortalSuspended) {
	.writeTrace(, , "PortalSuspended", nil)
}

func ( *tracer) ( byte,  int32,  *Query) {
	.writeTrace(, , "Query", func() {
		fmt.Fprintf(.buf, "\t %s", traceDoubleQuotedString([]byte(.String)))
	})
}

func ( *tracer) ( byte,  int32,  *ReadyForQuery) {
	.writeTrace(, , "ReadyForQuery", func() {
		fmt.Fprintf(.buf, "\t %c", .TxStatus)
	})
}

func ( *tracer) ( byte,  int32,  *RowDescription) {
	.writeTrace(, , "RowDescription", func() {
		fmt.Fprintf(.buf, "\t %d", len(.Fields))
		for ,  := range .Fields {
			fmt.Fprintf(.buf, ` %s %d %d %d %d %d %d`, traceDoubleQuotedString(.Name), .TableOID, .TableAttributeNumber, .DataTypeOID, .DataTypeSize, .TypeModifier, .Format)
		}
	})
}

func ( *tracer) ( byte,  int32,  *SSLRequest) {
	.writeTrace(, , "SSLRequest", nil)
}

func ( *tracer) ( byte,  int32,  *StartupMessage) {
	.writeTrace(, , "StartupMessage", nil)
}

func ( *tracer) ( byte,  int32,  *Sync) {
	.writeTrace(, , "Sync", nil)
}

func ( *tracer) ( byte,  int32,  *Terminate) {
	.writeTrace(, , "Terminate", nil)
}

func ( *tracer) ( byte,  int32,  string,  func()) {
	.mux.Lock()
	defer .mux.Unlock()
	defer func() {
		if .buf.Cap() > 1024 {
			.buf = &bytes.Buffer{}
		} else {
			.buf.Reset()
		}
	}()

	if !.SuppressTimestamps {
		 := time.Now()
		.buf.WriteString(.Format("2006-01-02 15:04:05.000000"))
		.buf.WriteByte('\t')
	}

	.buf.WriteByte()
	.buf.WriteByte('\t')
	.buf.WriteString()
	.buf.WriteByte('\t')
	.buf.WriteString(strconv.FormatInt(int64(), 10))

	if  != nil {
		()
	}

	.buf.WriteByte('\n')
	.buf.WriteTo(.w)
}

// traceDoubleQuotedString returns t.buf as a double-quoted string without any escaping. It is roughly equivalent to
// pqTraceOutputString in libpq.
func traceDoubleQuotedString( []byte) string {
	return `"` + string() + `"`
}

// traceSingleQuotedString returns buf as a single-quoted string with non-printable characters hex-escaped. It is
// roughly equivalent to pqTraceOutputNchar in libpq.
func traceSingleQuotedString( []byte) string {
	 := &strings.Builder{}

	.WriteByte('\'')
	for ,  := range  {
		if  < 32 ||  > 126 {
			fmt.Fprintf(, `\x%x`, )
		} else {
			.WriteByte()
		}
	}
	.WriteByte('\'')

	return .String()
}