// 14 february 2014

package ui

import (
	"fmt"
)

// A Listbox is a vertical list of items, of which either at most one or any number of items can be selected at any given time.
// On creation, no item is selected.
// For information on scrollbars, see "Scrollbars" in the Overview.
// Due to implementation issues, the presence of horizontal scrollbars is currently implementation-defined.
type Listbox struct {
	created   bool
	sysData   *sysData
	initItems []string
}

func newListbox(multiple bool, items ...string) (l *Listbox) {
	l = &Listbox{
		sysData:   mksysdata(c_listbox),
		initItems: items,
	}
	l.sysData.alternate = multiple
	return l
}

// NewListbox creates a new single-selection Listbox with the given items loaded initially.
func NewListbox(items ...string) *Listbox {
	return newListbox(false, items...)
}

// NewMultiSelListbox creates a new multiple-selection Listbox with the given items loaded initially.
func NewMultiSelListbox(items ...string) *Listbox {
	return newListbox(true, items...)
}

// Append adds items to the end of the Listbox's list.
// Append will panic if something goes wrong on platforms that do not abort themselves.
func (l *Listbox) Append(what ...string) {
	if l.created {
		for _, s := range what {
			l.sysData.append(s)
		}
		return
	}
	l.initItems = append(l.initItems, what...)
}

// InsertBefore inserts a new item in the Listbox before the item at the given position. It panics if the given index is out of bounds.
// InsertBefore will also panic if something goes wrong on platforms that do not abort themselves.
func (l *Listbox) InsertBefore(what string, before int) {
	var m []string

	if l.created {
		if before < 0 || before >= l.sysData.len() {
			goto badrange
		}
		l.sysData.insertBefore(what, before)
		return
	}
	if before < 0 || before >= len(l.initItems) {
		goto badrange
	}
	m = make([]string, 0, len(l.initItems)+1)
	m = append(m, l.initItems[:before]...)
	m = append(m, what)
	l.initItems = append(m, l.initItems[before:]...)
	return
badrange:
	panic(fmt.Errorf("index %d out of range in Listbox.InsertBefore()", before))
}

// Delete removes the given item from the Listbox. It panics if the given index is out of bounds.
func (l *Listbox) Delete(index int) {
	if l.created {
		if index < 0 || index >= l.sysData.len() {
			goto badrange
		}
		l.sysData.delete(index)
		return
	}
	if index < 0 || index >= len(l.initItems) {
		goto badrange
	}
	l.initItems = append(l.initItems[:index], l.initItems[index+1:]...)
	return
badrange:
	panic(fmt.Errorf("index %d out of range in Listbox.Delete()", index))
}

// Selection returns a list of strings currently selected in the Listbox, or an empty list if none have been selected. This list will have at most one item on a single-selection Listbox.
func (l *Listbox) Selection() []string {
	if l.created {
		return l.sysData.selectedTexts()
	}
	return nil
}

// SelectedIndices returns a list of the currently selected indexes in the Listbox, or an empty list if none have been selected. This list will have at most one item on a single-selection Listbox.
func (l *Listbox) SelectedIndices() []int {
	if l.created {
		return l.sysData.selectedIndices()
	}
	return nil
}

// Len returns the number of items in the Listbox.
//
// On platforms for which this function may return an error, it panics if one is returned.
func (l *Listbox) Len() int {
	if l.created {
		return l.sysData.len()
	}
	return len(l.initItems)
}

func (l *Listbox) make(window *sysData) (err error) {
	err = l.sysData.make(window)
	if err != nil {
		return err
	}
	for _, s := range l.initItems {
		l.sysData.append(s)
	}
	l.created = true
	return nil
}

func (l *Listbox) allocate(x int, y int, width int, height int, d *sysSizeData) []*allocation {
	return []*allocation{&allocation{
		x:       x,
		y:       y,
		width:   width,
		height:  height,
		this:		l,
	}}
}

func (l *Listbox) preferredSize(d *sysSizeData) (width int, height int) {
	return l.sysData.preferredSize(d)
}

func (l *Listbox) commitResize(a *allocation, d *sysSizeData) {
	l.sysData.commitResize(a, d)
}

func (l *Listbox) getAuxResizeInfo(d *sysSizeData) {
	l.sysData.getAuxResizeInfo(d)
}