package input

import (
	"git.gophernest.net/azpect/TextEditor/internal/action"
	"git.gophernest.net/azpect/TextEditor/internal/command"
	"git.gophernest.net/azpect/TextEditor/internal/motion"
	"git.gophernest.net/azpect/TextEditor/internal/operator"
)

type Keymap struct {
	motions   map[string]action.Motion
	operators map[string]action.Operator
	actions   map[string]action.Action // standalone actions: i.e., 'i', 'a'
}

func NewNormalKeymap() *Keymap {
	return &Keymap{
		motions: map[string]action.Motion{
			"j":      motion.MoveDown{Count: 1},
			"k":      motion.MoveUp{Count: 1},
			"h":      motion.MoveLeft{Count: 1},
			"l":      motion.MoveRight{Count: 1},
			"G":      motion.MoveToBottom{},
			"gg":     motion.MoveToTop{},
			"0":      motion.MoveToLineStart{},
			"$":      motion.MoveToLineEnd{},
			"_":      motion.MoveToLineContentStart{},
			"^":      motion.MoveToLineContentStart{},
			"|":      motion.MoveToColumn{Count: 0},
			"w":      motion.MoveForwardWord{Count: 1},
			"W":      motion.MoveForwardWORD{Count: 1},
			"e":      motion.MoveForwardWordEnd{Count: 1},
			"E":      motion.MoveForwardWORDEnd{Count: 1},
			"b":      motion.MoveBackwardWord{Count: 1},
			"ctrl+u": motion.ScrollUpHalfPage{},
			"ctrl+d": motion.ScrollDownHalfPage{},
		},
		operators: map[string]action.Operator{
			"d": operator.DeleteOperator{},
			"y": operator.YankOperator{},
			"c": operator.ChangeOperator{}, // TODO: Finish implementing
			// "s": SubstitueOp{},
			// "~": SwapCaseOp{},
		},
		actions: map[string]action.Action{
			"i":      action.EnterInsert{},
			"a":      action.EnterInsertAfter{},
			"I":      action.EnterInsertLineStart{},
			"A":      action.EnterInsertLineEnd{},
			"o":      action.OpenLineBelow{},
			"O":      action.OpenLineAbove{},
			"x":      action.DeleteChar{Count: 1},
			":":      action.EnterComandMode{},
			"v":      action.EnterVisualMode{},
			"V":      action.EnterVisualLineMode{},
			"ctrl+v": action.EnterVisualBlockMode{},
			"D":      action.DeleteToEndOfLine{Count: 1},
			"C":      action.ChangeToEndOfLine{Count: 1},
			"s":      action.SubstituteChar{Count: 1},
			"S":      action.SubstituteLine{Count: 1},
			"p":      action.Paste{Count: 1},
			"P":      action.PasteBefore{Count: 1},
		},
	}
}

func NewVisualKeymap() *Keymap {
	return &Keymap{
		motions: map[string]action.Motion{
			"j":  motion.MoveDown{Count: 1},
			"k":  motion.MoveUp{Count: 1},
			"h":  motion.MoveLeft{Count: 1},
			"l":  motion.MoveRight{Count: 1},
			"G":  motion.MoveToBottom{},
			"gg": motion.MoveToTop{},
			"0":  motion.MoveToLineStart{},
			"$":  motion.MoveToLineEnd{},
			"_":  motion.MoveToLineContentStart{},
			"^":  motion.MoveToLineContentStart{},
			"|":  motion.MoveToColumn{Count: 0},
			"w":  motion.MoveForwardWord{Count: 1},
			"W":  motion.MoveForwardWORD{Count: 1},
			"e":  motion.MoveForwardWordEnd{Count: 1},
			"E":  motion.MoveForwardWORDEnd{Count: 1},
			"b":  motion.MoveBackwardWord{Count: 1},
		},
		operators: map[string]action.Operator{
			"d": operator.DeleteOperator{},
			"x": operator.DeleteOperator{},
			"y": operator.YankOperator{},
			"c": operator.ChangeOperator{},
		},
		actions: map[string]action.Action{
			"p": action.VisualPaste{Count: 1},
			// ":":      action.EnterComandMode{}, // Different OP
		},
	}
}

func NewInsertKeymap() *Keymap {
	return &Keymap{
		motions: map[string]action.Motion{
			"down":  motion.MoveDown{Count: 1},
			"up":    motion.MoveUp{Count: 1},
			"left":  motion.MoveLeft{Count: 1},
			"right": motion.MoveRight{Count: 1},
		},
		operators: map[string]action.Operator{}, // this will likely be empty
		actions: map[string]action.Action{
			"enter":     action.InsertNewline{},
			"backspace": action.InsertBackspace{},
			"delete":    action.InsertDelete{},
			"tab":       action.InsertTab{},
			"ctrl+w":    action.InsertDeletePreviousWord{},
		},
	}

}

func NewCommandKeymap() *Keymap {
	return &Keymap{
		motions: map[string]action.Motion{
			"left":  motion.MoveCommandLeft{},
			"right": motion.MoveCommandRight{},
		},
		operators: map[string]action.Operator{}, // this will likely be empty
		actions: map[string]action.Action{
			"esc":       action.ExitCommandMode{},
			"enter":     action.CommandExecute{Registry: command.DefaultRegistry},
			"backspace": action.CommandBackspace{},
			"delete":    action.CommandDelete{},
			"ctrl+w":    action.CommandDeletePreviousWord{},
		},
	}

}

// Lookup returns what type of binding a key is
func (km *Keymap) Lookup(key string) (kind string, value any) {
	if m, ok := km.motions[key]; ok {
		return "motion", m
	}
	if o, ok := km.operators[key]; ok {
		return "operator", o
	}
	if a, ok := km.actions[key]; ok {
		return "action", a
	}
	return "", nil
}

// HasPrefix returns true if any binding starts with this prefix
func (km *Keymap) HasPrefix(prefix string) bool {
	for key := range km.motions {
		if len(key) > len(prefix) && key[:len(prefix)] == prefix {
			return true
		}
	}
	for key := range km.operators {
		if len(key) > len(prefix) && key[:len(prefix)] == prefix {
			return true
		}
	}
	for key := range km.actions {
		if len(key) > len(prefix) && key[:len(prefix)] == prefix {
			return true
		}
	}
	return false
}
