// Copyright (c) 2015-2023 Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
// resty source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.

package resty

import (
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
)

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// Logger interface
//_______________________________________________________________________

// Logger interface is to abstract the logging from Resty. Gives control to
// the Resty users, choice of the logger.
type Logger interface {
	Errorf(format string, v ...interface{})
	Warnf(format string, v ...interface{})
	Debugf(format string, v ...interface{})
}

func createLogger() *logger {
	 := &logger{l: log.New(os.Stderr, "", log.Ldate|log.Lmicroseconds)}
	return 
}

var _ Logger = (*logger)(nil)

type logger struct {
	l *log.Logger
}

func ( *logger) ( string,  ...interface{}) {
	.output("ERROR RESTY "+, ...)
}

func ( *logger) ( string,  ...interface{}) {
	.output("WARN RESTY "+, ...)
}

func ( *logger) ( string,  ...interface{}) {
	.output("DEBUG RESTY "+, ...)
}

func ( *logger) ( string,  ...interface{}) {
	if len() == 0 {
		.l.Print()
		return
	}
	.l.Printf(, ...)
}

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// Rate Limiter interface
//_______________________________________________________________________

type RateLimiter interface {
	Allow() bool
}

var ErrRateLimitExceeded = errors.New("rate limit exceeded")

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// Package Helper methods
//_______________________________________________________________________

// IsStringEmpty method tells whether given string is empty or not
func ( string) bool {
	return len(strings.TrimSpace()) == 0
}

// DetectContentType method is used to figure out `Request.Body` content type for request header
func ( interface{}) string {
	 := plainTextType
	 := kindOf()
	switch  {
	case reflect.Struct, reflect.Map:
		 = jsonContentType
	case reflect.String:
		 = plainTextType
	default:
		if ,  := .([]byte);  {
			 = http.DetectContentType()
		} else if  == reflect.Slice {
			 = jsonContentType
		}
	}

	return 
}

// IsJSONType method is to check JSON content type or not
func ( string) bool {
	return jsonCheck.MatchString()
}

// IsXMLType method is to check XML content type or not
func ( string) bool {
	return xmlCheck.MatchString()
}

// Unmarshalc content into object from JSON or XML
func ( *Client,  string,  []byte,  interface{}) ( error) {
	if IsJSONType() {
		 = .JSONUnmarshal(, )
	} else if IsXMLType() {
		 = .XMLUnmarshal(, )
	}

	return
}

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// RequestLog and ResponseLog type
//_______________________________________________________________________

// RequestLog struct is used to collected information from resty request
// instance for debug logging. It sent to request log callback before resty
// actually logs the information.
type RequestLog struct {
	Header http.Header
	Body   string
}

// ResponseLog struct is used to collected information from resty response
// instance for debug logging. It sent to response log callback before resty
// actually logs the information.
type ResponseLog struct {
	Header http.Header
	Body   string
}

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// Package Unexported methods
//_______________________________________________________________________

// way to disable the HTML escape as opt-in
func jsonMarshal( *Client,  *Request,  interface{}) (*bytes.Buffer, error) {
	if !.jsonEscapeHTML || !.jsonEscapeHTML {
		return noescapeJSONMarshal()
	}

	,  := .JSONMarshal()
	if  != nil {
		return nil, 
	}

	 := acquireBuffer()
	_, _ = .Write()
	return , nil
}

func firstNonEmpty( ...string) string {
	for ,  := range  {
		if !IsStringEmpty() {
			return 
		}
	}
	return ""
}

var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")

func escapeQuotes( string) string {
	return quoteEscaper.Replace()
}

func createMultipartHeader(, ,  string) textproto.MIMEHeader {
	 := make(textproto.MIMEHeader)

	var  string
	if IsStringEmpty() {
		 = fmt.Sprintf(`form-data; name="%s"`, )
	} else {
		 = fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
			, escapeQuotes())
	}
	.Set("Content-Disposition", )

	if !IsStringEmpty() {
		.Set(hdrContentTypeKey, )
	}
	return 
}

func addMultipartFormField( *multipart.Writer,  *MultipartField) error {
	,  := .CreatePart(createMultipartHeader(.Param, .FileName, .ContentType))
	if  != nil {
		return 
	}

	_,  = io.Copy(, .Reader)
	return 
}

func writeMultipartFormFile( *multipart.Writer, ,  string,  io.Reader) error {
	// Auto detect actual multipart content type
	 := make([]byte, 512)
	,  := .Read()
	if  != nil &&  != io.EOF {
		return 
	}

	,  := .CreatePart(createMultipartHeader(, , http.DetectContentType()))
	if  != nil {
		return 
	}

	if _,  = .Write([:]);  != nil {
		return 
	}

	_,  = io.Copy(, )
	return 
}

func addFile( *multipart.Writer, ,  string) error {
	,  := os.Open()
	if  != nil {
		return 
	}
	defer closeq()
	return writeMultipartFormFile(, , filepath.Base(), )
}

func addFileReader( *multipart.Writer,  *File) error {
	return writeMultipartFormFile(, .ParamName, .Name, .Reader)
}

func getPointer( interface{}) interface{} {
	 := valueOf()
	if .Kind() == reflect.Ptr {
		return 
	}
	return reflect.New(.Type()).Interface()
}

func isPayloadSupported( string,  bool) bool {
	return !( == MethodHead ||  == MethodOptions || ( == MethodGet && !))
}

func typeOf( interface{}) reflect.Type {
	return indirect(valueOf()).Type()
}

func valueOf( interface{}) reflect.Value {
	return reflect.ValueOf()
}

func indirect( reflect.Value) reflect.Value {
	return reflect.Indirect()
}

func kindOf( interface{}) reflect.Kind {
	return typeOf().Kind()
}

func createDirectory( string) ( error) {
	if _,  = os.Stat();  != nil {
		if os.IsNotExist() {
			if  = os.MkdirAll(, 0755);  != nil {
				return
			}
		}
	}
	return
}

func canJSONMarshal( string,  reflect.Kind) bool {
	return IsJSONType() && ( == reflect.Struct ||  == reflect.Map ||  == reflect.Slice)
}

func functionName( interface{}) string {
	return runtime.FuncForPC(reflect.ValueOf().Pointer()).Name()
}

func acquireBuffer() *bytes.Buffer {
	return bufPool.Get().(*bytes.Buffer)
}

func releaseBuffer( *bytes.Buffer) {
	if  != nil {
		.Reset()
		bufPool.Put()
	}
}

// requestBodyReleaser wraps requests's body and implements custom Close for it.
// The Close method closes original body and releases request body back to sync.Pool.
type requestBodyReleaser struct {
	releaseOnce sync.Once
	reqBuf      *bytes.Buffer
	io.ReadCloser
}

func newRequestBodyReleaser( io.ReadCloser,  *bytes.Buffer) io.ReadCloser {
	if  == nil {
		return 
	}

	return &requestBodyReleaser{
		reqBuf:     ,
		ReadCloser: ,
	}
}

func ( *requestBodyReleaser) () error {
	 := .ReadCloser.Close()
	.releaseOnce.Do(func() {
		releaseBuffer(.reqBuf)
	})

	return 
}

func closeq( interface{}) {
	if ,  := .(io.Closer);  {
		silently(.Close())
	}
}

func silently( ...interface{}) {}

func composeHeaders( *Client,  *Request,  http.Header) string {
	 := make([]string, 0, len())
	for ,  := range sortHeaderKeys() {
		 = append(, "\t"+strings.TrimSpace(fmt.Sprintf("%25s: %s", , strings.Join([], ", "))))
	}
	return strings.Join(, "\n")
}

func sortHeaderKeys( http.Header) []string {
	 := make([]string, 0, len())
	for  := range  {
		 = append(, )
	}
	sort.Strings()
	return 
}

func copyHeaders( http.Header) http.Header {
	 := http.Header{}
	for ,  := range  {
		[] = 
	}
	return 
}

type noRetryErr struct {
	err error
}

func ( *noRetryErr) () string {
	return .err.Error()
}

func wrapNoRetryErr( error) error {
	if  != nil {
		 = &noRetryErr{err: }
	}
	return 
}

func unwrapNoRetryErr( error) error {
	if ,  := .(*noRetryErr);  {
		 = .err
	}
	return 
}