package uiterm

import (
	"strings"

	"github.com/nsf/termbox-go"
)

type TreeItem interface {
	TreeItemStyle(fg, bg Attribute, active bool) (Attribute, Attribute)
	String() string
}

type renderedTreeItem struct {
	//String string
	Level int
	Item  TreeItem
}

type Tree struct {
	Fg, Bg            Attribute
	Generator         func(item TreeItem) []TreeItem
	KeyListener       func(ui *Ui, tree *Tree, item TreeItem, key Key)
	CharacterListener func(ui *Ui, tree *Tree, item TreeItem, chr rune)

	lines      []renderedTreeItem
	activeLine int

	ui             *Ui
	active         bool
	x0, y0, x1, y1 int
}

func bounded(i, lower, upper int) int {
	if i < lower {
		return lower
	}
	if i > upper {
		return upper
	}
	return i
}

func (t *Tree) uiInitialize(ui *Ui) {
	t.ui = ui
}

func (t *Tree) uiSetActive(active bool) {
	t.active = active
	t.uiDraw()
}

func (t *Tree) uiSetBounds(x0, y0, x1, y1 int) {
	t.x0 = x0
	t.y0 = y0
	t.x1 = x1
	t.y1 = y1
	t.uiDraw()
}

func (t *Tree) Rebuild() {
	if t.Generator == nil {
		t.lines = []renderedTreeItem{}
		return
	}

	lines := []renderedTreeItem{}
	for _, item := range t.Generator(nil) {
		children := t.rebuild_rec(item, 0)
		if children != nil {
			lines = append(lines, children...)
		}
	}
	t.lines = lines
	t.SetActiveLine(0, true)
	t.uiDraw()
}

func (t *Tree) rebuild_rec(parent TreeItem, level int) []renderedTreeItem {
	if parent == nil {
		return nil
	}
	lines := []renderedTreeItem{
		renderedTreeItem{
			Level: level,
			Item:  parent,
		},
	}
	for _, item := range t.Generator(parent) {
		children := t.rebuild_rec(item, level+1)
		if children != nil {
			lines = append(lines, children...)
		}
	}
	return lines
}

func (t *Tree) uiDraw() {
	t.ui.beginDraw()
	defer t.ui.endDraw()

	if t.lines == nil {
		t.Rebuild()
	}

	if t.y1-t.y0 <= 0 {
		return
	}

	var line = t.activeLine
	var height = t.y1 - t.y0
	var startline = 0
	//var total = len(t.lines)
	//I'd welcome a better algorithm for this; for that matter, I'd love a book or reference for all sorts of GUI algorithms.
	//if (startline+height) < line {
	for startline = 0; (startline + height) <= line; startline += height {
	}
	//}
	//if startline+height >= total {
	//var rem=(startline+height)-total
	//startline-=rem
	//}
	if startline < 0 {
		startline = 0
	}
	line = startline
	for y := t.y0; y < t.y1; y++ {
		var reader *strings.Reader
		var item TreeItem
		level := 0
		if line < len(t.lines) {
			item = t.lines[line].Item
			level = t.lines[line].Level
			reader = strings.NewReader(item.String())
		}
		for x := t.x0; x < t.x1; x++ {
			var chr rune = ' '
			fg := t.Fg
			bg := t.Bg
			dx := x - t.x0
			dy := y - t.y0
			if reader != nil && level*2 <= dx {
				if ch, _, err := reader.ReadRune(); err == nil {
					chr = ch
					fg, bg = item.TreeItemStyle(fg, bg, t.active && t.activeLine == dy)
				}
			}
			termbox.SetCell(x, y, chr, termbox.Attribute(fg), termbox.Attribute(bg))
		}
		if t.activeLine == (line) {
			termbox.SetCursor(t.x0, y)
		}
		line++
	}
}

func (t *Tree) SetActiveLine(num int, relative bool) {
	if relative {
		t.activeLine = bounded(t.activeLine+num, 0, len(t.lines)-1)
	} else {
		t.activeLine = bounded(num, 0, len(t.lines)-1)
	}
}

func (t *Tree) uiKeyEvent(key Key) {
	var runHandler = true
	switch key {
	case KeyArrowUp:
		t.SetActiveLine(-1, true)
		runHandler = false
	case KeyArrowDown:
		t.SetActiveLine(1, true)
		runHandler = false
	}
	if runHandler == true && t.KeyListener != nil {
		t.KeyListener(t.ui, t, t.lines[t.activeLine].Item, key)
	}
	t.uiDraw()
}

func (t *Tree) uiCharacterEvent(ch rune) {
	if t.CharacterListener != nil {
		t.CharacterListener(t.ui, t, t.lines[t.activeLine].Item, ch)
	}
}
