package keymap

import (
	
	

	
	
	
)

// MatchLocal incrementally attempts to match cached input keys against the local keymap.
// Returns the bind if matched, the corresponding command, and if we only matched by prefix.
func ( *Engine) ( inputrc.Bind,  func(),  bool) {
	if .local == "" {
		return , , 
	}

	// Several local keymaps are empty by default: instead we use restricted
	// lists of commands, regardless of the key-sequence their bound to.
	 := .getContextBinds(false)
	if len() == 0 {
		return , , 
	}

	, , ,  := .dispatchKeys()

	if !.Macro {
		 = .commands[.Action]
	}

	if  {
		core.MatchedPrefix(.keys, ...)
	} else {
		core.MatchedKeys(.keys, , [len():]...)
	}

	// Similarly to the MatchMain() function, give a special treatment to the escape key
	// (if it's alone): using escape in Viopp/menu-complete/isearch should cancel the
	// current mode, thus we return either a Vim movement-mode command, or nothing.
	if .isEscapeKey() && ( ||  == nil) {
		, ,  = .handleEscape(false)
	}

	return , , 
}

// MatchMain incrementally attempts to match cached input keys against the local keymap.
// Returns the bind if matched, the corresponding command, and if we only matched by prefix.
func ( *Engine) ( inputrc.Bind,  func(),  bool) {
	if .main == "" {
		return , , 
	}

	// Get all binds present in the main keymap. Here, contrary
	// to the local keymap matching, no keymap should be empty.
	 := .getContextBinds(true)
	if len() == 0 {
		return , , 
	}

	// Find the target action, macro or command.
	, , ,  := .dispatchKeys()

	if !.Macro {
		 = .commands[.Action]
	}

	// In the main menu, all keys that have been tested against
	// the binds will be dropped after command execution (whether
	// or not there's actually a command to execute).
	if  {
		core.MatchedPrefix(.keys, ...)
	} else {
		core.MatchedKeys(.keys, )
	}

	// Non-incremental search mode should always insert the keys
	// if they did not exactly match one of the valid commands.
	if .nonIncSearch && ( == nil || ) {
		 = inputrc.Bind{Action: "self-insert"}
		.active = 
		 = .resolve()
		 = false
	}

	// Adjusting for the ESC key: when convert-meta is enabled,
	// many binds will actually match ESC as a prefix. This makes
	// commands like vi-movement-mode unreachable, so if the bind
	// is vi-movement-mode, we return it to be ran regardless of
	// the other binds matching by prefix.
	if .isEscapeKey() && !.IsEmacs() &&  {
		, ,  = .handleEscape(true)
	}

	return , , 
}

func ( *Engine) ( map[string]inputrc.Bind) ( inputrc.Bind,  bool, ,  []byte) {
	// Support for Unicode: if the character is multi-byte (UTF-8), consume all its bytes
	// and treat it as a single self-insert action. Note that we just peek the characters
	// here, so if it's actually not a UTF-8 character, we just keep going and re-peek later.
	,  := core.PeekChar(.keys)
	if  {
		return .active, , , 
	}

	if len() > 1 {
		 = append(, ...)
		 := inputrc.Bind{
			Action: "self-insert",
			Macro:  false,
		}

		 = append(, ...)
		 = .makeMatch(, inputrc.Bind{})
		core.PopChar(.keys)

		return .active, , , 
	}

	for {
		// Read a single byte from the input buffer.
		// This mimics the way Bash reads input when the inputrc option `byte-oriented` is set.
		// This is because the default binds map is built with byte sequences, not runes, and this
		// has some implications if the terminal is sending 8-bit characters (extended alphabet).
		,  := core.PeekKey(.keys)
		if  {
			break
		}

		 = append(, )

		,  := .matchBind(, )

		// If the current keys have no matches but the previous
		// matching process found a prefix, use it with the keys.
		if .Action == "" && len() == 0 {
			 = .makeMatch(.prefixed, inputrc.Bind{})

			// FIX related to Github issue #73, where someone
			// complains not being able to input Unicode characters
			// correctly. Explanation:
			// The call to PeekKey at the beginning of this function
			// used to be PopKey. We don't pop the key unless we have
			// an empty byte.
			core.PopKey(.keys)

			break
		}

		// FIX related to Github issue #73, also pop the key here.
		core.PopKey(.keys)

		// From here, there is at least one bind matched, by prefix
		// or exactly, so the key we popped is considered matched.
		 = append(, )

		// If we matched a prefix, keep the matched bind for later.
		if len() > 0 {
			 = true

			if .Action != "" {
				.prefixed = 
			}

			continue
		}

		// Or an exact match, so drop any prefixed one.
		 = .makeMatch(, inputrc.Bind{})

		break
	}

	return .active, , , 
}

func ( *Engine) ( []byte,  map[string]inputrc.Bind) (inputrc.Bind, []inputrc.Bind) {
	var  inputrc.Bind
	var  []inputrc.Bind

	// Make a sorted list with all keys in the binds map.
	var  []string
	for  := range  {
		 = append(, )
	}

	// Sort the list of sequences by alphabetical order and length.
	sort.Slice(, func(,  int) bool {
		if len([]) == len([]) {
			return [] < []
		}

		return len([]) < len([])
	})

	// Iterate over the sorted list of sequences and find all binds
	// that match the sequence either by prefix or exactly.
	for ,  := range  {
		 := strutil.ConvertMeta([]rune())

		if len(string()) < len() && strings.HasPrefix(, string()) {
			 = append(, [])
		}

		if string() ==  {
			 = []
		}
	}

	return , 
}

func ( *Engine) ( inputrc.Bind) func() {
	if .Macro {
		return nil
	}

	if .Action == "" {
		return nil
	}

	return .commands[.Action]
}

// handleEscape is used to override or change the matched command when the escape key has
// been pressed: it might exit completion/isearch menus, use the vi-movement-mode, etc.
func ( *Engine) ( bool) ( inputrc.Bind,  func(),  bool) {
	switch {
	case .prefixed.Action == "vi-movement-mode":
		// The vi-movement-mode command always has precedence over
		// other binds when we are currently using the main keymap.
		 = .prefixed

	case ! && .IsEmacs() && .Local() == Isearch:
		// There is no dedicated "soft-escape" of the incremental-search
		// mode when in Emacs keymap, so we use the escape key to cancel
		// the search and return to the main keymap.
		 = inputrc.Bind{Action: "emacs-editing-mode"}

		core.PopForce(.keys)

	case !:
		// When using the local keymap, we simply drop any prefixed
		// or matched bind, so that the key will be matched against
		// the main keymap: between both, completion/isearch menus
		// will likely be cancelled.
		 = inputrc.Bind{}
	}

	// Drop what needs to, and resolve the command.
	.prefixed = inputrc.Bind{}

	if .Action != "" && !.Macro {
		 = .resolve()
	}

	// Drop the escape key in the stack
	if  {
		core.PopForce(.keys)
	}

	return , , 
}

// makeMatch populates currently used binds.
func ( *Engine) (,  inputrc.Bind) ( bool) {
	.active = 
	.prefixed = 
	return .prefixed.Action != ""
}

func ( *Engine) () bool {
	 := .keys.Caller()
	if len() == 0 {
		return false
	}

	if len() > 1 {
		return false
	}

	if [0] != inputrc.Esc {
		return false
	}

	return true
}