package ui

import (
	"fmt"
	"github.com/jroimartin/gocui"
	"strings"
	"unicode"
)

var translate = map[string]string{
	"/":        "Slash",
	"\\":       "Backslash",
	"[":        "LsqBracket",
	"]":        "RsqBracket",
	"_":        "Underscore",
	"escape":   "Esc",
	"~":        "Tilde",
	"pageup":   "Pgup",
	"pagedown": "Pgdn",
	"pgup":     "Pgup",
	"pgdown":   "Pgdn",
	// "up":       "ArrowUp",
	// "down":     "ArrowDown",
	// "right":    "ArrowRight",
	// "left":     "ArrowLeft",
	"ctl": "Ctrl",
}

var display = map[string]string{
	"Slash":      "/",
	"Backslash":  "\\",
	"LsqBracket": "[",
	"RsqBracket": "]",
	"Underscore": "_",
	"Tilde":      "~",
	"Ctrl":       "^",
}

var supportedKeybindings = map[string]gocui.Key{
	"KeyF1":     gocui.KeyF1,
	"KeyF2":     gocui.KeyF2,
	"KeyF3":     gocui.KeyF3,
	"KeyF4":     gocui.KeyF4,
	"KeyF5":     gocui.KeyF5,
	"KeyF6":     gocui.KeyF6,
	"KeyF7":     gocui.KeyF7,
	"KeyF8":     gocui.KeyF8,
	"KeyF9":     gocui.KeyF9,
	"KeyF10":    gocui.KeyF10,
	"KeyF11":    gocui.KeyF11,
	"KeyF12":    gocui.KeyF12,
	"KeyInsert": gocui.KeyInsert,
	"KeyDelete": gocui.KeyDelete,
	"KeyHome":   gocui.KeyHome,
	"KeyEnd":    gocui.KeyEnd,
	"KeyPgup":   gocui.KeyPgup,
	"KeyPgdn":   gocui.KeyPgdn,
	// "KeyArrowUp":        gocui.KeyArrowUp,
	// "KeyArrowDown":      gocui.KeyArrowDown,
	// "KeyArrowLeft":      gocui.KeyArrowLeft,
	// "KeyArrowRight":     gocui.KeyArrowRight,
	"KeyCtrlTilde":      gocui.KeyCtrlTilde,
	"KeyCtrl2":          gocui.KeyCtrl2,
	"KeyCtrlSpace":      gocui.KeyCtrlSpace,
	"KeyCtrlA":          gocui.KeyCtrlA,
	"KeyCtrlB":          gocui.KeyCtrlB,
	"KeyCtrlC":          gocui.KeyCtrlC,
	"KeyCtrlD":          gocui.KeyCtrlD,
	"KeyCtrlE":          gocui.KeyCtrlE,
	"KeyCtrlF":          gocui.KeyCtrlF,
	"KeyCtrlG":          gocui.KeyCtrlG,
	"KeyBackspace":      gocui.KeyBackspace,
	"KeyCtrlH":          gocui.KeyCtrlH,
	"KeyTab":            gocui.KeyTab,
	"KeyCtrlI":          gocui.KeyCtrlI,
	"KeyCtrlJ":          gocui.KeyCtrlJ,
	"KeyCtrlK":          gocui.KeyCtrlK,
	"KeyCtrlL":          gocui.KeyCtrlL,
	"KeyEnter":          gocui.KeyEnter,
	"KeyCtrlM":          gocui.KeyCtrlM,
	"KeyCtrlN":          gocui.KeyCtrlN,
	"KeyCtrlO":          gocui.KeyCtrlO,
	"KeyCtrlP":          gocui.KeyCtrlP,
	"KeyCtrlQ":          gocui.KeyCtrlQ,
	"KeyCtrlR":          gocui.KeyCtrlR,
	"KeyCtrlS":          gocui.KeyCtrlS,
	"KeyCtrlT":          gocui.KeyCtrlT,
	"KeyCtrlU":          gocui.KeyCtrlU,
	"KeyCtrlV":          gocui.KeyCtrlV,
	"KeyCtrlW":          gocui.KeyCtrlW,
	"KeyCtrlX":          gocui.KeyCtrlX,
	"KeyCtrlY":          gocui.KeyCtrlY,
	"KeyCtrlZ":          gocui.KeyCtrlZ,
	"KeyEsc":            gocui.KeyEsc,
	"KeyCtrlLsqBracket": gocui.KeyCtrlLsqBracket,
	"KeyCtrl3":          gocui.KeyCtrl3,
	"KeyCtrl4":          gocui.KeyCtrl4,
	"KeyCtrlBackslash":  gocui.KeyCtrlBackslash,
	"KeyCtrl5":          gocui.KeyCtrl5,
	"KeyCtrlRsqBracket": gocui.KeyCtrlRsqBracket,
	"KeyCtrl6":          gocui.KeyCtrl6,
	"KeyCtrl7":          gocui.KeyCtrl7,
	"KeyCtrlSlash":      gocui.KeyCtrlSlash,
	"KeyCtrlUnderscore": gocui.KeyCtrlUnderscore,
	"KeySpace":          gocui.KeySpace,
	"KeyBackspace2":     gocui.KeyBackspace2,
	"KeyCtrl8":          gocui.KeyCtrl8,
}

type Key struct {
	value    gocui.Key
	modifier gocui.Modifier
	tokens   []string
	input    string
}

func getKeybinding(input string) (Key, error) {
	f := func(c rune) bool { return unicode.IsSpace(c) || c == '+' }
	tokens := strings.FieldsFunc(input, f)
	var normalizedTokens []string
	var modifier = gocui.ModNone

	for _, token := range tokens {
		normalized := strings.ToLower(token)

		if value, exists := translate[normalized]; exists {
			normalized = value
		} else {
			normalized = strings.Title(normalized)
		}

		if normalized == "Alt" {
			modifier = gocui.ModAlt
			continue
		}

		if len(normalized) == 1 {
			normalizedTokens = append(normalizedTokens, strings.ToUpper(normalized))
			continue
		}

		normalizedTokens = append(normalizedTokens, normalized)
	}

	lookup := "Key" + strings.Join(normalizedTokens, "")

	if key, exists := supportedKeybindings[lookup]; exists {
		return Key{key, modifier, normalizedTokens, input}, nil
	}

	if modifier != gocui.ModNone {
		return Key{0, modifier, normalizedTokens, input}, fmt.Errorf("unsupported keybinding: %s (+%+v)", lookup, modifier)
	}
	return Key{0, modifier, normalizedTokens, input}, fmt.Errorf("unsupported keybinding: %s", lookup)
}

func getKeybindings(input string) []Key {
	ret := make([]Key, 0)
	for _, value := range strings.Split(input, ",") {
		key, err := getKeybinding(value)
		if err != nil {
			panic(fmt.Errorf("could not parse keybinding '%s' from request '%s': %+v", value, input, err))
		}
		ret = append(ret, key)
	}
	if len(ret) == 0 {
		panic(fmt.Errorf("must have at least one keybinding"))
	}
	return ret
}

func (key Key) String() string {
	displayTokens := make([]string, 0)
	prefix := ""
	for _, token := range key.tokens {
		if token == "Ctrl" {
			prefix = "^"
			continue
		}
		if value, exists := display[token]; exists {
			token = value
		}
		displayTokens = append(displayTokens, token)
	}
	return prefix + strings.Join(displayTokens, "+")
}