package door

import (
	"log"
	"strings"
	"unicode"
)

/*
type MenuOption struct {
	Ch   rune
	Text string
}
*/

type Menu struct {
	Chosen      int
	SelectedR   ColorRender
	UnselectedR ColorRender
	Options     []rune
	Activated   func(int, *Door) // Which option is active
	// MenuOptions []MenuOption
	Panel
}

func MakeMenuRender(bracketColor, optionColor, upperColor, lowerColor string) ColorRender {
	f := func(text string) string {
		var output string
		var lastColor string
		option := true
		for _, c := range text {
			if option {
				if c == '[' || c == ']' {
					if lastColor != bracketColor {
						output += bracketColor
						lastColor = bracketColor
					}
					output += string(c)
					option = (c == '[')
				} else {
					if lastColor != optionColor {
						output += optionColor
						lastColor = optionColor
					}
					output += string(c)
				}
			} else {
				if unicode.IsUpper(c) {
					if lastColor != upperColor {
						output += upperColor
						lastColor = upperColor
					}
					output += string(c)
				} else {
					if lastColor != lowerColor {
						output += lowerColor
						lastColor = lowerColor
					}
					output += string(c)
				}
			}
		}
		return output
	}
	return f
}

func (m *Menu) AddSelection(key string, text string) {
	key = key[:1] // Make sure it is just 1 character
	m.Options = append(m.Options, rune(key[0]))

	// 4 = [ key ] + " "
	if len(text)+4 > m.Width {
		log.Panicf("Menu (not wide enough) Width %d : text size %d + 4 = %d\n", m.Width, len(text), len(text)+4)
	}

	linetext := "[" + key + "] " + text + strings.Repeat(" ", m.Width-(4+len(text)))
	m.Lines = append(m.Lines, Line{Text: linetext, RenderF: m.UnselectedR})
}

// Should I be using this, or write a function like the original --
// m.AddSelection("K", "Text") to build?  (And throw away the
// redundant) MenuOptions/MenuOption parts?  They do let me create
// the menu in larger chunks -- but it is redundant.
// Once the menu is built, I really could delete the MenuOptions.

/*
func (m *Menu) Build() {
	// Take MenuOptions and build the Menu

	// Reset
	m.Lines = make([]Line, 0)
	m.Options = make([]rune, 0)
	m.Chosen = 0

	for _, option := range m.MenuOptions {
		m.Options = append(m.Options, option.Ch)
		text := "[" + string(option.Ch) + "] " + option.Text + strings.Repeat(" ", m.Width-(4+len(option.Text)))
		m.Lines = append(m.Lines, Line{Text: text, RenderF: m.UnselectedR})
	}
}
*/

// Get the character that was pressed from the choice
func (m *Menu) GetOption(choice int) rune {
	return m.Options[choice-1]
}

func (m *Menu) Choose(d *Door) int {
	var changed []int
	updated := true
	update_and_exit := false
	blank := ColorText("BLACK")

	for {
		if updated {
			for x := range m.Lines {
				if x == m.Chosen {
					m.Lines[x].RenderF = m.SelectedR
				} else {
					m.Lines[x].RenderF = m.UnselectedR
				}
			}
			if len(changed) == 0 {
				d.Write(m.Output() + blank)
			} else {
				// Update just the lines that have changed
				for _, line := range changed {
					d.Write(m.UpdateLine(line))
				}
				d.Write(m.GotoEnd() + blank)
			}
			if m.Activated != nil {
				m.Activated(m.Chosen, d)
			}
		}

		if update_and_exit {
			return m.Chosen + 1
		}

		updated = false

		var r rune
		var ex Extended
		var err error
		r, ex, err = d.WaitKey(DefaultTimeout)

		if err != nil {
			return -1
		}
		previous_choice := m.Chosen
		changed = make([]int, 0)

		use_numberpad := true
		for _, option := range m.Options {
			if option == rune('8') || option == rune('2') {
				use_numberpad = false
			}
		}

		if ex == MOUSE {
			mouse, ok := d.GetMouse()
			if ok {
				if mouse.Button == 65 {
					// Translate Mouse Wheel Up
					ex = UP_ARROW
				}
				if mouse.Button == 66 {
					// Translate Mouse Wheel Down
					ex = DOWN_ARROW
				}
				// Look at Mouse X/Y to determine where they clicked
				if mouse.Button == 1 || mouse.Button == 2 {
					// TODO:
					log.Println("Mouse (", mouse.X, ",", mouse.Y, ")")
				}
			}
		}

		switch ex {
		case UP_ARROW:
			if m.Chosen > 0 {
				m.Chosen--
				updated = true
			}
		case DOWN_ARROW:
			if m.Chosen < len(m.Lines)-1 {
				m.Chosen++
				updated = true
			}
		case HOME:
			if m.Chosen != 0 {
				m.Chosen = 0
				updated = true
			}
		case END:
			if m.Chosen != len(m.Lines)-1 {
				m.Chosen = len(m.Lines) - 1
				updated = true
			}
		}

		switch r {
		case '8':
			if use_numberpad {
				if m.Chosen > 0 {
					m.Chosen--
					updated = true
				}
			}
		case '2':
			if use_numberpad {

				if m.Chosen < len(m.Lines)-1 {
					m.Chosen++
					updated = true
				}
			}
		case 0x0d:
			// use current selection
			return m.Chosen + 1
		default:
			// Is the key in the list of options?

			// fmt.Printf("Event: %d\n", event)
			for x, option := range m.Options {
				// fmt.Printf("Checking %d, %d\n", x, option)
				if unicode.ToUpper(option) == unicode.ToUpper(r) {
					if m.Chosen == x {
						return x + 1
					}

					updated = true
					m.Chosen = x
					update_and_exit = true
				}
			}

		}

		if previous_choice != m.Chosen {
			changed = append(changed, previous_choice)
			changed = append(changed, m.Chosen)
			updated = true
		}
	}
}