package main

import (
	"strconv"
	"time"

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

const (
	DefaultExpireTime = time.Second / 2
)

type EventBuffer struct {
	UpdateTime time.Time
	TimeoutLen time.Duration
	Buffer     []termbox.Event
	CursorPos  int
}

func NewEventBuffer() *EventBuffer {
	return &EventBuffer{
		TimeoutLen: DefaultExpireTime,
		CursorPos:  -1,
	}
}

func (eb *EventBuffer) checkExpiration() {
	if eb.TimeoutLen != -1 && time.Now().Sub(eb.UpdateTime) > eb.TimeoutLen {
		eb.Clear()
	}
}

func (eb *EventBuffer) StartsWith(r rune) bool {
	eb.checkExpiration()
	return len(eb.Buffer) > 0 && eb.Buffer[0].Ch == r
}

func (eb *EventBuffer) Add(event termbox.Event) {
	eb.checkExpiration()
	if event.Key == termbox.KeyCtrlU {
		eb.Clear()
	} else if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 {
		eb.Backspace()
	} else {
		eb.Buffer = append(eb.Buffer, event)
	}
	eb.UpdateTime = time.Now()
}

func (eb *EventBuffer) Backspace() {
	eb.checkExpiration()
	if len(eb.Buffer) >= 1 {
		eb.Buffer = eb.Buffer[:len(eb.Buffer)-1]
	}
	eb.UpdateTime = time.Now()
}

func (eb *EventBuffer) MoveCursorBack() {
	if eb.CursorPos > 0 {
		eb.CursorPos--
	}
}

func (eb *EventBuffer) MoveCursorForward() {
	if eb.CursorPos < len(eb.Buffer) {
		eb.CursorPos++
	}
}

func (eb *EventBuffer) SetCursorToStart() {
	eb.CursorPos = 0
}

func (eb *EventBuffer) SetCursorToEnd() {
	eb.CursorPos = len(eb.Buffer)
}

func (eb *EventBuffer) Clear() {
	eb.Buffer = nil
	eb.UpdateTime = time.Now()
}

func (eb *EventBuffer) Events() []termbox.Event {
	eb.checkExpiration()
	return eb.Buffer
}

func (eb *EventBuffer) String() string {
	eb.checkExpiration()
	var ret string
	for _, v := range eb.Buffer {
		if v.Ch != 0 {
			ret = ret + string(v.Ch)
		} else {
			switch v.Key {
			case termbox.KeySpace:
				ret = ret + " "
			case termbox.KeyArrowUp:
				ret = ret + string(ChUpKeyValue)
			case termbox.KeyArrowRight:
				ret = ret + string(ChRightKeyValue)
			case termbox.KeyArrowDown:
				ret = ret + string(ChDownKeyValue)
			case termbox.KeyArrowLeft:
				ret = ret + string(ChLeftKeyValue)
			case termbox.KeyEnter:
				ret = ret + string(ChEnterKeyValue)
			}
		}
	}
	return ret
}

func (eb *EventBuffer) SetToString(s string) {
	eb.Clear()
	for _, v := range s {
		if v == '\n' {
			eb.Add(termbox.Event{Ch: 0, Key: termbox.KeyEnter})
		} else {
			eb.Add(termbox.Event{Ch: v})
		}
	}
}

func (eb *EventBuffer) MatchesString(s string) bool {
	eb.checkExpiration()
	if len(s) != len(eb.Buffer) {
		return false
	}
	return s == eb.String()
}

func (eb *EventBuffer) MatchesEvents(events []termbox.Event) bool {
	eb.checkExpiration()
	if len(events) != len(eb.Buffer) {
		return false
	}
	for k := range events {
		if events[k].Ch != eb.Buffer[k].Ch || events[k].Key != eb.Buffer[k].Key {
			return false
		}
	}
	return true
}

func (eb *EventBuffer) StartsWithKey(key termbox.Key) bool {
	eb.checkExpiration()
	if len(eb.Buffer) > 0 {
		return eb.Buffer[0].Key == key
	}
	return false
}

func (eb *EventBuffer) Size() int {
	eb.checkExpiration()
	return len(eb.Buffer)
}

func (eb *EventBuffer) IsNumber() bool {
	_, err := strconv.Atoi(eb.String())
	return err == nil
}

func (eb *EventBuffer) Number() (int, error) {
	return strconv.Atoi(eb.String())
}

func (eb *EventBuffer) OptNumber(def int) int {
	ret, err := strconv.Atoi(eb.String())
	if err != nil {
		return def
	}
	return ret
}
