package accesslog

import (
	"bytes"
	"io"
	"text/template"
)

// Template is a Formatter.
// It's used to print the Log in a text/template way.
// The caller has full control over the printable result;
// certain fields can be ignored, change the display order and e.t.c.
//
// For faster execution you can create a custom Formatter
// and compile your own quicktemplate: https://github.com/valyala/quicktemplate
//
// This one uses the standard text/template syntax.
type Template struct {
	// Custom template source.
	// Use this or `Tmpl/TmplName` fields.
	Text string
	// Custom template funcs to used when `Text` is not empty.
	Funcs template.FuncMap

	// Custom template to use, overrides the `Text` and `Funcs` fields.
	Tmpl *template.Template
	// If not empty then this named template/block renders the log line.
	TmplName string

	ac *AccessLog
}

// SetOutput creates the default template if missing
// when this formatter is registered.
func (f *Template) SetOutput(dest io.Writer) {
	f.ac, _ = dest.(*AccessLog)

	if f.Tmpl == nil {
		tmpl := template.New("")

		text := f.Text
		if text != "" {
			tmpl.Funcs(f.Funcs)
		} else {
			text = defaultTmplText
		}

		f.Tmpl = template.Must(tmpl.Parse(text))
	}
}

const defaultTmplText = "{{.Now.Format .TimeFormat}}|{{.Latency}}|{{.Code}}|{{.Method}}|{{.Path}}|{{.IP}}|{{.RequestValuesLine}}|{{.BytesReceivedLine}}|{{.BytesSentLine}}|{{.Request}}|{{.Response}}|\n"

func (f *Template) LogText(log *Log) (string, error) {
	var err error

	// A template may be executed safely in parallel, although if parallel
	// executions share a Writer the output may be interleaved.
	// We solve that using a buffer pool, no locks when template is executing (x2 performance boost).
	temp := f.ac.bufPool.Get().(*bytes.Buffer)

	if f.TmplName != "" {
		err = f.Tmpl.ExecuteTemplate(temp, f.TmplName, log)
	} else {
		err = f.Tmpl.Execute(temp, log)
	}

	if err != nil {
		f.ac.bufPool.Put(temp)
		return "", err
	}

	text := temp.String()
	temp.Reset()
	f.ac.bufPool.Put(temp)

	return text, nil
}

// Format prints the logs in text/template format.
func (f *Template) Format(log *Log) (bool, error) {
	var err error

	// A template may be executed safely in parallel, although if parallel
	// executions share a Writer the output may be interleaved.
	// We solve that using a buffer pool, no locks when template is executing (x2 performance boost).
	temp := f.ac.bufPool.Get().(*bytes.Buffer)

	if f.TmplName != "" {
		err = f.Tmpl.ExecuteTemplate(temp, f.TmplName, log)
	} else {
		err = f.Tmpl.Execute(temp, log)
	}

	if err != nil {
		f.ac.bufPool.Put(temp)
		return true, err
	}

	f.ac.Write(temp.Bytes())
	temp.Reset()
	f.ac.bufPool.Put(temp)
	return true, nil
}
