package widgets

import (
	"log"

	"github.com/dweymouth/supersonic/ui/layouts"
	myTheme "github.com/dweymouth/supersonic/ui/theme"
	"github.com/dweymouth/supersonic/ui/util"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/driver/desktop"
	"fyne.io/fyne/v2/layout"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
)

type SortType int

const (
	SortNone SortType = iota
	SortAscending
	SortDescending
)

type ListHeaderSort struct {
	ColNumber int
	Type      SortType
}

type ListColumn struct {
	Text             string
	Alignment        fyne.TextAlign
	CanToggleVisible bool
}

type ListHeader struct {
	widget.BaseWidget

	DisableSorting bool

	OnColumnSortChanged         func(ListHeaderSort)
	OnColumnVisibilityChanged   func(int, bool)
	OnColumnVisibilityMenuShown func(*widget.PopUp)

	sort          ListHeaderSort
	columns       []ListColumn
	columnVisible []bool
	columnsLayout *layouts.ColumnsLayout

	columnsContainer *fyne.Container
	container        *fyne.Container
	popUpMenu        *fyne.Container
}

func NewListHeader(cols []ListColumn, layout *layouts.ColumnsLayout) *ListHeader {
	l := &ListHeader{
		columns:          cols,
		columnsLayout:    layout,
		columnsContainer: container.New(layout),
	}
	l.columnVisible = make([]bool, len(cols))
	for i := range l.columnVisible {
		l.columnVisible[i] = true
	}
	rect := myTheme.NewThemedRectangle(myTheme.ColorNameListHeader)
	rect.CornerRadiusName = theme.SizeNameSelectionRadius
	l.container = container.NewStack(rect, l.columnsContainer)
	l.ExtendBaseWidget(l)
	l.buildColumns()
	return l
}

func (l *ListHeader) SetColumnVisible(colNum int, visible bool) {
	if colNum >= len(l.columns) {
		log.Println("error: ListHeader.SetColumnVisible: column index out of range")
		return
	}
	if visible {
		l.columnsContainer.Objects[colNum].Show()
	} else {
		l.columnsContainer.Objects[colNum].Hide()
	}
	l.columnVisible[colNum] = visible
	l.columnsContainer.Refresh()
}

func (l *ListHeader) buildColumns() {
	for i, c := range l.columns {
		hdr := newColHeader(c, &l.DisableSorting)
		hdr.OnSortChanged = func(i int) func(SortType) {
			return func(sort SortType) { l.SetSorting(ListHeaderSort{ColNumber: i, Type: sort}) }
		}(i)
		hdr.OnTappedSecondary = l.TappedSecondary
		l.columnsContainer.Add(hdr)
	}
}

// Sets the sorting for the ListHeader. Will invoke
// OnColumnSortChanged if set.
func (l *ListHeader) SetSorting(sort ListHeaderSort) {
	if l.sort == sort {
		return
	}
	l.sort = sort
	for i, c := range l.columnsContainer.Objects {
		c.(*colHeader).hovered = false // reset hovered status
		if i == sort.ColNumber {
			c.(*colHeader).Sort = sort.Type
		} else {
			c.(*colHeader).Sort = SortNone
		}
	}
	l.Refresh()
	if l.OnColumnSortChanged != nil {
		l.OnColumnSortChanged(sort)
	}
}

func (l *ListHeader) CreateRenderer() fyne.WidgetRenderer {
	return widget.NewSimpleRenderer(l.container)
}

func (l *ListHeader) TappedSecondary(e *fyne.PointEvent) {
	l.setupPopUpMenu()
	if len(l.popUpMenu.Objects) == 0 {
		return
	}
	pop := widget.NewPopUp(l.popUpMenu, fyne.CurrentApp().Driver().CanvasForObject(l))
	pop.ShowAtPosition(e.AbsolutePosition)
	if l.OnColumnVisibilityMenuShown != nil {
		l.OnColumnVisibilityMenuShown(pop)
	}
}

func (l *ListHeader) setupPopUpMenu() {
	if l.popUpMenu == nil {
		l.popUpMenu = container.New(&layouts.VboxCustomPadding{ExtraPad: -10})
		for i, c := range l.columns {
			if c.CanToggleVisible {
				l.popUpMenu.Add(widget.NewCheck(c.Text, l.createOnChangedCallbk(i)))
			}
		}
	}
	objIdx := 0
	for i, col := range l.columns {
		if col.CanToggleVisible {
			l.popUpMenu.Objects[objIdx].(*widget.Check).Checked = l.columnVisible[i]
			objIdx++
		}
	}
}

func (l *ListHeader) createOnChangedCallbk(colNum int) func(bool) {
	return func(val bool) {
		l.columnVisible[colNum] = val
		l.SetColumnVisible(colNum, val)
		if l.OnColumnVisibilityChanged != nil {
			l.OnColumnVisibilityChanged(colNum, val)
		}
	}
}

type colHeader struct {
	widget.BaseWidget

	Sort              SortType
	OnSortChanged     func(SortType)
	OnTappedSecondary func(*fyne.PointEvent)

	sortDisabled *bool
	columnCfg    ListColumn
	hovered      bool

	label             *widget.RichText
	sortIconContainer *fyne.Container
	sortIconNegSpacer fyne.CanvasObject
	container         *fyne.Container
}

func newColHeader(columnCfg ListColumn, sortDisabled *bool) *colHeader {
	c := &colHeader{columnCfg: columnCfg, sortDisabled: sortDisabled}
	c.ExtendBaseWidget(c)

	c.label = widget.NewRichTextWithText(columnCfg.Text)
	c.label.Segments[0].(*widget.TextSegment).Style = widget.RichTextStyle{
		TextStyle: fyne.TextStyle{Bold: true},
		Alignment: columnCfg.Alignment,
	}
	r := myTheme.NewThemedRectangle(myTheme.ColorNameListHeader)
	r.Translucent = true
	c.sortIconContainer = container.NewStack(widget.NewIcon(theme.MenuDropDownIcon()), r)
	// hack to remove extra icon space
	// should be hidden whenever sortIcon is hidden
	c.sortIconNegSpacer = util.NewHSpace(0)

	return c
}

var _ desktop.Hoverable = (*colHeader)(nil)

func (c *colHeader) MouseIn(*desktop.MouseEvent) {
	if !*c.sortDisabled {
		c.hovered = true
		c.Refresh()
	}
}

func (c *colHeader) MouseOut() {
	c.hovered = false
	c.Refresh()
}

func (c *colHeader) MouseMoved(*desktop.MouseEvent) {
	// intentionally left blank
}

func (c *colHeader) Tapped(*fyne.PointEvent) {
	if *c.sortDisabled {
		return
	}
	c.hovered = false // stop showing the next-up sort icon
	c.Sort = nextSortType(c.Sort)
	c.Refresh()
	if c.OnSortChanged != nil {
		c.OnSortChanged(c.Sort)
	}
}

func (c *colHeader) TappedSecondary(e *fyne.PointEvent) {
	if c.OnTappedSecondary != nil {
		c.OnTappedSecondary(e)
	}
}

func (c *colHeader) Refresh() {
	sort := c.Sort
	if c.hovered {
		sort = nextSortType(sort)
	}
	if sort == SortDescending {
		c.sortIconContainer.Objects[0].(*widget.Icon).Resource = theme.MenuDropDownIcon()
	} else {
		c.sortIconContainer.Objects[0].(*widget.Icon).Resource = theme.MenuDropUpIcon()
	}
	c.sortIconContainer.Objects[1].(*myTheme.ThemedRectangle).Hidden = !c.hovered

	if sort > 0 && c.sortIconContainer.Hidden {
		c.sortIconContainer.Hidden = false
		c.container.Add(c.sortIconNegSpacer)
	} else if (sort == SortNone || *c.sortDisabled) && !c.sortIconContainer.Hidden {
		c.sortIconContainer.Hidden = true
		c.container.Remove(c.sortIconNegSpacer)
	}

	c.BaseWidget.Refresh()
}

func (c *colHeader) CreateRenderer() fyne.WidgetRenderer {
	if c.container == nil {
		c.container = container.New(&layouts.HboxCustomPadding{DisableThemePad: true, ExtraPad: -8})
		if c.columnCfg.Alignment != fyne.TextAlignLeading {
			c.container.Add(layout.NewSpacer())
		}
		c.container.Add(c.label)
		c.container.Add(c.sortIconContainer)
		c.container.Add(c.sortIconNegSpacer)
		if c.columnCfg.Alignment == fyne.TextAlignCenter {
			c.container.Add(layout.NewSpacer())
		}
	}
	return widget.NewSimpleRenderer(c.container)
}

func nextSortType(in SortType) SortType {
	switch in {
	case SortNone:
		return SortAscending
	case SortAscending:
		return SortDescending
	case SortDescending:
		return SortNone
	default:
		log.Println("Unreachable")
		return SortNone
	}
}
