package ui

import (
	"fmt"
	"image"

	"code.ur.gs/lupine/ordoor/internal/assetstore"
	"code.ur.gs/lupine/ordoor/internal/menus"
)

// listBox is a TListBox in VCL terms. It has a number of lines of text, one of
// which may be selected, and a slider with up and down buttons to scroll if the
// options in the box exceed its viewing capacity.
//
// TODO: multi-select functionality? Is it needed?
type listBox struct {
	locator string

	upBtn   *button
	downBtn *button

	// FIXME: can we share code between slider and this element?
	thumbBase *assetstore.Sprite // Bounds are given by this
	thumbImg  *assetstore.Sprite // This is displayed at offset * (height / steps)

	lines []*noninteractive // We display to these

	// The list box acts as a window onto these
	strings []string

	// The start of our window
	offset int
}

func (d *Driver) buildListBox(group *menus.Group, up, down, thumb *menus.Record, items ...*menus.Record) (*listBox, *Widget, error) {
	upElem, upWidget, err := d.buildButton(up.Props())
	if err != nil {
		return nil, nil, err
	}

	downElem, downWidget, err := d.buildButton(down.Props())
	if err != nil {
		return nil, nil, err
	}

	thumbBaseSpr, err := d.menu.Sprite(thumb.ObjectIdx, thumb.Share)
	if err != nil {
		return nil, nil, err
	}

	thumbImgSpr, err := d.menu.Sprite(thumb.ObjectIdx, thumb.BaseSpriteID())
	if err != nil {
		return nil, nil, err
	}

	element := &listBox{
		locator: group.Locator,
		// TODO: upBtn needs to be frozen when offset == 0; downBtn when offset == max
		upBtn:   upElem,
		downBtn: downElem,

		// TODO: need to be able to drag the thumb
		thumbBase: thumbBaseSpr,
		thumbImg:  thumbImgSpr,
	}

	// Internal wiring-up
	upElem.onClick(element.up)
	downElem.onClick(element.down)

	// FIXME: Test data for now
	for i := 0; i < 50; i++ {
		element.strings = append(element.strings, fmt.Sprintf("FOO %v", i))
	}

	// Register everything. Since we're a composite of other controls, they are
	// mostly self-registered at the moment.
	widget := &Widget{
		Children:      []*Widget{upWidget, downWidget},
		Active:        group.Active, // FIXME: children have their own active state
		ownPaintables: []paintable{element},
		ownValueables: []valueable{element},
	}

	// FIXME: we should be able to freeze/unfreeze as a group.

	// HURK: These need to be registered after the other elements so they are
	// drawn in the correct order to be visible
	for _, rec := range items {
		ni, niWidget, err := d.buildStatic(rec.Props())
		if err != nil {
			return nil, nil, err
		}
		niWidget.ownClickables = append(niWidget.ownClickables, ni)

		// TODO: pick the correct font
		ni.label = &label{
			align: AlignModeLeft,
			font:  d.menu.Font(0),
			rect:  ni.rect,
		}
		element.lines = append(element.lines, ni)
		widget.Children = append(widget.Children, niWidget)
	}

	element.refresh()

	return element, widget, nil
}

func (l *listBox) id() string {
	return l.locator
}

func (l *listBox) value() string {
	return ""
}

func (l *listBox) setValue(s string) {
}

func (l *listBox) SetStrings(to []string) {
	if len(to) < len(l.strings) {
		l.offset = 0 // FIXME: unconditional? Trim to max?
	}

	l.strings = to

	l.refresh()
}

// TODO: Selected returns the index and value of the selected item
func (l *listBox) Selected() (int, string) {
	return 0, ""
}

func (l *listBox) up() {
	if l.offset <= 0 {
		return
	}

	l.offset -= 1
	l.refresh()
}

func (l *listBox) down() {
	if l.offset > len(l.strings)-len(l.lines) {
		return
	}

	l.offset += 1
	l.refresh()
}

func (l *listBox) refresh() {
	for i, ni := range l.lines {
		// FIXME: noninteractive isn't set up for dynamic text yet. Need to
		// generate textImg on demand instead of once at start.
		if ni.label != nil {
			ni.label.text = ""
			if len(l.strings) > l.offset+i {
				ni.label.text = l.strings[l.offset+i]
			}
		}
	}
}

func (l *listBox) thumbPos() image.Point {
	pos := l.thumbImg.Rect.Min
	if len(l.strings) == 0 {
		return pos
	}

	pixPerLine := (l.thumbBase.Rect.Dy()) / (len(l.strings) - len(l.lines))
	pos.Y += pixPerLine * l.offset

	return pos
}

func (l *listBox) regions(tick int) []region {
	// Draw the slider at the appropriate point
	out := oneRegion(l.thumbBase.Rect.Min, l.thumbBase.Image)
	out = append(out, oneRegion(l.thumbPos(), l.thumbImg.Image)...)

	return out
}
