package traces

import (
	
	_ 
	
	
	

	
	. 
	

	am 
)

// StateTrace is a single-state trace with spans.
type StateTrace struct {
	// state name
	State string
	// state active during the current tx
	Active bool
	// Events are point-in-time annotations attached to this span.
	Spans []TraceSpan
}

// TraceSpan is a lifespan inside a trace of a single state.
type TraceSpan struct {
	Name string
	Desc string
	// Start machine time.
	Start uint64
	// End machine time.
	End    uint64
	Events []SpanEvent
}

// SpanEvent is an event inside a span.
type SpanEvent struct {
	Name string
	Desc string
	// Start machine time.
	Start uint64
	// End machine time.
	End uint64
}

//go:embed traces.html
var pageHtml string

// TODO define schema
var stateUpdateTraces = "UpdateTraces"

// HtmlPage wraps the rendered trace component into a full HTML document.
func ( string) string {
	return strings.Replace(pageHtml, "{{body}}", , 1)
}

// NewPage creates a live trace page served under prefix. A new set of demo
// spans is generated periodically.
func (
	 context.Context,  *am.Machine,  string,
) (*livepage.Page[*View], error) {
	 := &View{mach: , Zoom: 5}

	,  := livepage.New(, , livepage.Opts{
		HtmlPage: HtmlPage,
		Debounce: 50 * time.Millisecond,
		Prefix:   ,
		DiffHtml: true,
	})
	if  != nil {
		return nil, 
	}

	return , nil
}

type View struct {
	livepage.Compo

	Zoom int

	mach      *am.Machine
	spans     []StateTrace
	currMTime uint64
	colors    map[string]string
	query     string
}

func ( *View) () *am.Machine {
	return .mach
}

// SetSpans replaces the rendered span data.
func ( *View) ( []StateTrace) {
	.spans = 
}

// SetStateColors sets state colors for the current transaction.
func ( *View) ( map[string]string) {
	.colors = 
}

// SetCurrentTime sets the marker for the selected transition.
func ( *View) ( uint64) {
	.currMTime = 
}

func ( *View) ( string) string {
	if ,  := .colors[];  &&  != "" {
		return 
	}
	return "#555b66"
}

func ( *View) () UI {
	,  := .spanRange()
	 := make([]UI, len(.spans))
	for ,  := range .visibleSpans() {
		[] = Div().Class("row").Body(
			.traceLabel(),
			.traceSpanCell(, , ),
		)
	}

	 := Div().Class("rows")
	if len() == 0 {
		 = .Body(Div().Class("empty").Textf("No spans match %q.", .query))
	} else {
		 = .Body(...)
	}

	return Div().Class("shell").Body(
		Div().Class("topbar").Body(
			.renderMeta(, ),
			.renderRuler(, ),
		),
		,
	)
}

func ( *View) (,  uint64) UI {
	 := .Zoom
	if  < 1 {
		 = 1
	}

	return Div().Class("meta").Body(
		Div().Class("btn-group").Body(
			Button().
				Class("btn").
				Title("Zoom out").
				OnClick(func(Context, Event) {
					if .Zoom > 1 {
						.Zoom--
						.mach.Add1(stateUpdateTraces, nil)
					}
				}).
				Text("-"),
			Span().Class("btn-label").Textf("%dx", ),
			Button().
				Class("btn").
				Title("Zoom in").
				OnClick(func(Context, Event) {
					if .Zoom < 10 {
						.Zoom++
						.mach.Add1(stateUpdateTraces, nil)
					}
				}).
				Text("+"),
		),
		Div().Class("mach-times").Body(
			Span().Class("mach-time").Textf("From t%s", formatMachineTime()),
			Span().Class("mach-time").Textf("To   t%s", formatMachineTime()),
		),
	)
}

func ( *View) (,  uint64) UI {
	 := make([]UI, 0, 10)
	 :=  - 
	for  := 0;  <= 4; ++ {
		 := float64() * 25
		 := fmt.Sprintf("%.4f%%", )
		 := map[string]string{"left": }
		switch  {
		case 0:
			["transform"] = "none"
		case 4:
			["transform"] = "translateX(-100%)"
		}

		 = append(,
			Div().Class("tick").Styles(map[string]string{"left": }),
			Span().Class("ruler-label").
				Styles().
				Text("t"+formatMachineTime(+*uint64()/4)),
		)
	}
	return Div().Class("ruler").Body(...)
}

func ( *View) ( StateTrace) UI {
	 := .stateColor(.State)
	return Div().
		Class("state").
		Body(Div().Class("state-body").Body(
			Div().Class("state-head").Body(
				Span().Class("name").Style("color", ).Text(.State),
			),
		))
}

func ( *View) ( StateTrace, ,  uint64) UI {
	 :=  - 
	if  == 0 {
		 = 1
	}

	 := make([]UI, len(.Spans)+1)
	for ,  := range .Spans {
		 := float64(.Start-) / float64() * 100
		 := float64(.End-.Start) / float64() * 100

		[] = Div().Class("bar").
			Styles(map[string]string{
				"left":       fmt.Sprintf("%.4f%%", ),
				"width":      fmt.Sprintf("%.4f%%", ),
				"background": "gray",
			}).
			Title(spanTitle(.State, ))
	}

	if .currMTime >=  && .currMTime <=  {
		 := float64(.currMTime-) / float64() * 100
		 = append(, Div().
			Class("now-line").
			Style("left", fmt.Sprintf("%.4f%%", )))
	}

	return Div().Class("track").Body(...)
}

func ( *View) () (uint64, uint64) {
	 := .spans
	var ,  uint64
	var  bool
	for ,  := range  {
		for ,  := range .Spans {
			if ! || .Start <  {
				 = .Start
			}
			if ! || .End >  {
				 = .End
			}
			 = true
		}
	}
	if ! {
		return 0, 0
	}
	return , 
}

func ( *View) () []StateTrace {
	 := .spans
	 := strings.ToLower(strings.TrimSpace(.query))
	if  == "" {
		return 
	}

	 := make([]StateTrace, 0, len())
	for ,  := range  {
		 := strings.ToLower(.State)
		for ,  := range .Spans {
			 += " " + strings.ToLower(.Name+" "+.Desc)
		}
		if strings.Contains(, ) {
			 = append(, )
		}
	}
	return 
}

func spanTitle( string,  TraceSpan) string {
	 := ""
	if .Name != "" {
		 += .Name + " | "
	}
	return fmt.Sprintf("%st%s to t%s = %s ticks",
		,
		formatMachineTime(.Start),
		formatMachineTime(.End),
		formatMachineTime(.End-.Start),
	)
}

func formatMachineTime( uint64) string {
	return utils.Fmt.Sprintf("%v", )
}