// 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 benchstat

import (
	"bytes"
	"strings"

	"github.com/google/safehtml"
	"github.com/google/safehtml/template"
)

var htmlTemplate = template.Must(template.New("").Funcs(htmlFuncs).Parse(`
{{- if . -}}
{{with index . 0}}
<table class='benchstat {{if .OldNewDelta}}oldnew{{end}}'>
{{if eq (len .Configs) 1}}
{{- else -}}
<tr class='configs'><th>{{range .Configs}}<th>{{.}}{{end}}
{{end}}
{{end}}
{{- range $i, $table := .}}
<tbody>
{{if eq (len .Configs) 1}}
<tr><th><th>{{.Metric}}
{{else -}}
<tr><th><th colspan='{{len .Configs}}' class='metric'>{{.Metric}}{{if .OldNewDelta}}<th>delta{{end}}
{{end}}{{range $group := group $table.Rows -}}
{{if and (gt (len $table.Groups) 1) (len (index . 0).Group)}}<tr class='group'><th colspan='{{colspan (len $table.Configs) $table.OldNewDelta}}'>{{(index . 0).Group}}{{end}}
{{- range $row := . -}}
{{if $table.OldNewDelta -}}
<tr class='{{if eq .Change 1}}better{{else if eq .Change -1}}worse{{else}}unchanged{{end}}'>
{{- else -}}
<tr>
{{- end -}}
<td>{{.Benchmark}}{{range .Metrics}}<td>{{.Format $row.Scaler}}{{end}}{{if $table.OldNewDelta}}<td class='{{if eq .Delta "~"}}nodelta{{else}}delta{{end}}'>{{replace .Delta "-" "−" -1}}<td class='note'>{{.Note}}{{end}}
{{end -}}
{{- end -}}
<tr><td>&nbsp;
</tbody>
{{end}}
</table>
{{end -}}
`))

var htmlFuncs = template.FuncMap{
	"replace": strings.Replace,
	"group":   htmlGroup,
	"colspan": htmlColspan,
}

func htmlColspan(configs int, delta bool) int {
	if delta {
		configs++
	}
	return configs + 1
}

func htmlGroup(rows []*Row) (out [][]*Row) {
	var group string
	var cur []*Row
	for _, r := range rows {
		if r.Group != group {
			group = r.Group
			if len(cur) > 0 {
				out = append(out, cur)
				cur = nil
			}
		}
		cur = append(cur, r)
	}
	if len(cur) > 0 {
		out = append(out, cur)
	}
	return
}

// SafeFormatHTML returns the HTML formatting of the tables.
func SafeFormatHTML(tables []*Table) safehtml.HTML {
	h, err := htmlTemplate.ExecuteToHTML(tables)
	if err != nil {
		// Only possible errors here are template not matching data structure.
		// Don't make caller check - it's our fault.
		panic(err)
	}
	return h
}

// FormatHTML appends an HTML formatting of the tables to buf.
func FormatHTML(buf *bytes.Buffer, tables []*Table) {
	err := htmlTemplate.Execute(buf, tables)
	if err != nil {
		// Only possible errors here are template not matching data structure.
		// Don't make caller check - it's our fault.
		panic(err)
	}
}
