package rich

import (
	"github.com/diamondburned/cchat-gtk/internal/ui/rich/parser/markup"
	"github.com/diamondburned/cchat/text"
	"github.com/gotk3/gotk3/gtk"
	"github.com/gotk3/gotk3/pango"
)

// LabelRenderer is the input/output function to render a rich text segment to
// Pango markup.
type LabelRenderer = func(text.Rich) markup.RenderOutput

// RenderSkipImages is a label renderer that skips images.
func RenderSkipImages(rich text.Rich) markup.RenderOutput {
	return markup.RenderCmplxWithConfig(rich, markup.RenderConfig{
		SkipImages:     true,
		NoMentionLinks: true,
	})
}

// Label provides an abstraction around a regular GTK label that can be
// self-updated. Widgets that extend off of this (such as ToggleButton) does not
// need to manually
type Label struct {
	gtk.Label
	Tooltip bool

	label  text.Rich
	output markup.RenderOutput
	render LabelRenderer
}

var _ gtk.IWidget = (*Label)(nil)

// NewStaticLabel creates a static, non-updating label.
func NewStaticLabel(rich text.Rich) *Label {
	label, _ := gtk.LabelNew("")
	label.SetXAlign(0) // left align
	label.SetEllipsize(pango.ELLIPSIZE_END)

	if !rich.IsEmpty() {
		label.SetMarkup(markup.Render(rich))
	}

	return &Label{Label: *label, Tooltip: true}
}

// NewLabel creates a self-updating label.
func NewLabel(state LabelStateStorer) *Label {
	return NewLabelWithRenderer(state, nil)
}

// NewLabelWithRenderer creates a self-updating label using the given renderer.
func NewLabelWithRenderer(state LabelStateStorer, r LabelRenderer) *Label {
	l := NewStaticLabel(text.Plain(""))
	l.render = r
	state.OnUpdate(func() { l.SetLabel(state.Label()) })
	return l
}

// Output returns the rendered output.
func (l *Label) Output() markup.RenderOutput {
	return l.output
}

// SetLabel sets the label in the current thread, meaning it's not thread-safe.
func (l *Label) SetLabel(content text.Rich) {
	// Save a call if the content is empty.
	if content.IsEmpty() {
		l.label = content
		l.output = markup.RenderOutput{}

		return
	}

	l.label = content

	var out markup.RenderOutput
	if l.render != nil {
		out = l.render(content)
	} else {
		out = markup.RenderCmplx(content)
	}

	l.output = out
	l.SetMarkup(out.Markup)

	if l.Tooltip {
		l.SetTooltipMarkup(out.Markup)
	}
}

// SetRenderer sets a custom renderer. If the given renderer is nil, then the
// default markup renderer is used instead. The label is automatically updated.
func (l *Label) SetRenderer(renderer LabelRenderer) {
	l.render = renderer
	l.SetLabel(l.label)
}
