package core

import (
	
	
	
	
	

	
	
)

const (
	keyScanBufSize = 1024
)

// Stdin is used by the Keys struct to read and write keys.
// It can be overwritten to use other file descriptors or
// custom io.Readers, such as the one used on Windows.
var Stdin io.ReadCloser = os.Stdin

var rxRcvCursorPos = regexp.MustCompile(`\x1b\[([0-9]+);([0-9]+)R`)

// Keys is used read, manage and use keys input by the shell user.
type Keys struct {
	buf       []byte      // Keys read and waiting to be used.
	matched   []rune      // Keys that have been successfully matched against a bind.
	macroKeys []rune      // Keys that have been fed by a macro.
	mustWait  bool        // Keys are in the stack, but we must still read stdin.
	waiting   bool        // Currently waiting for keys on stdin.
	reading   bool        // Currently reading keys out of the main loop.
	keysOnce  chan []byte // Passing keys from the main routine.
	cursor    chan []byte // Cursor coordinates has been read on stdin.
	resize    chan bool   // Resize events on Windows are sent on stdin. USED IN WINDOWS

	cfg   *inputrc.Config // Configuration file used for meta key settings
	mutex sync.RWMutex    // Concurrency safety
}

// WaitAvailableKeys waits until an input key is either read from standard input,
// or directly returns if the key stack still/already has available keys.
func ( *Keys,  *inputrc.Config) {
	.cfg = 

	if len(.buf) > 0 && !.mustWait {
		return
	}

	// The macro engine might have fed some keys
	if len(.macroKeys) > 0 {
		return
	}

	.mutex.Lock()
	.waiting = true
	.cursor = make(chan []byte)
	.mutex.Unlock()

	defer func() {
		.mutex.Lock()
		.waiting = false
		.mutex.Unlock()
	}()

	for {
		// Start reading from os.Stdin in the background.
		// We will either read keyBuf from user, or an EOF
		// send by ourselves, because we pause reading.
		,  := .readInputFiltered()
		if  != nil && errors.Is(, io.EOF) {
			return
		}

		if len() == 0 {
			continue
		}

		switch {
		case .reading:
			.keysOnce <- 
			continue

		default:
			// When convert-meta is on, any meta-prefixed bind should
			// be stripped and replaced with an escape meta instead.
			if .cfg != nil && .cfg.GetBool("convert-meta") {
				 = []byte(strutil.ConvertMeta([]rune(string())))
			}

			.mutex.RLock()
			.buf = append(.buf, ...)
			.mutex.RUnlock()
		}

		return
	}
}

// PopKey is used to pop a key off the key stack without
// yet marking this key as having matched a bind command.
func ( *Keys) ( byte,  bool) {
	switch {
	case len(.buf) > 0:
		 = .buf[0]
		.buf = .buf[1:]
	case len(.macroKeys) > 0:
		 = byte(.macroKeys[0])
		.macroKeys = .macroKeys[1:]
	default:
		return byte(0), true
	}

	return , false
}

// PeekKey returns the first key in the stack, without removing it.
func ( *Keys) ( byte,  bool) {
	switch {
	case len(.buf) > 0:
		 = .buf[0]
	case len(.macroKeys) > 0:
		 = byte(.macroKeys[0])
	default:
		return byte(0), true
	}

	return , false
}

// MatchedKeys is used to indicate how many keys have been evaluated against the shell
// commands in the dispatching process (regardless of if a command was matched or not).
// This function should normally not be used by external users of the library.
func ( *Keys,  []byte,  ...byte) {
	if len() > 0 {
		.matched = []rune(string())
	}

	if len() > 0 {
		.buf = append(, .buf...)
	}

	.mustWait = false
}

// MatchedPrefix is similar to MatchedKeys, except that the provided keys
// should not be flushed, since they only matched some binds by prefix and
// that we need more keys for an exact match (or failure).
func ( *Keys,  ...byte) {
	if len() == 0 {
		return
	}

	.mutex.Lock()
	defer .mutex.Unlock()

	// Our keys are still considered unread, but they have been:
	// if there is no more keys in the stack, the next blocking
	// call to WaitAvailableKeys() should block for new keys.
	.mustWait = len(.buf) == 0
	.buf = append(, .buf...)
	.matched = []rune(string())
}

// PopForce is used to force-remove a key from the buffer, without marking
// it as having matched a bind command. This is used, for example, when the
// escape has been handled specially as a Vim escape.
func ( *Keys) ( byte,  bool) {
	switch {
	case len(.buf) > 0:
		 = .buf[0]
		.buf = .buf[1:]
	case len(.macroKeys) > 0:
		 = byte(.macroKeys[0])
		.macroKeys = .macroKeys[1:]
	default:
		return byte(0), true
	}

	// Force the macro recorder to use the matched keys.
	.mustWait = false

	return , false
}

// MacroKeys returns the keys that have matched a given command, and thus can be recorded
// as a part of the current macro. This function is different from keys.Caller() in that it
// won't return keys that have only matched a prefix, to avoid recording them twice.
func ( *Keys) []rune {
	if .mustWait {
		return nil
	}

	return .matched
}

// FlushUsed drops the keys that have matched a given command.
func ( *Keys) {
	.mutex.Lock()
	.matched = nil
	defer .mutex.Unlock()
}

// ReadKey reads keys from stdin like Read(), but immediately
// returns them instead of storing them in the stack, along with
// an indication on whether this key is an escape/abort one.
func ( *Keys) () ( rune,  bool) {
	.mutex.RLock()
	.keysOnce = make(chan []byte)
	.reading = true
	.mutex.RUnlock()

	defer func() {
		.mutex.RLock()
		.reading = false
		.mutex.RUnlock()
	}()

	switch {
	case len(.macroKeys) > 0:
		 = .macroKeys[0]
		.macroKeys = .macroKeys[1:]

	case .waiting:
		 := <-.keysOnce
		 = []rune(string())[0]
	default:
		,  := .readInputFiltered()
		 = []rune(string())[0]
	}

	// Always mark those keys as matched, so that
	// if the macro engine is recording, it will
	// capture them
	.matched = append(.matched, )

	return ,  == inputrc.Esc
}

// Pop removes the first byte in the key stack (first read) and returns it.
// It returns either a key and the empty boolean set to false, or if no keys
// are present, returns a zero rune and empty set to true.
// The key bytes returned by this function are not those that have been
// matched against the current command. The keys returned here are only
// keys that have not yet been dispatched. (ex: di" will match vim delete-to,
// then select-inside, but the quote won't match a command and will be passed
// to select-inside. This function Pop() will thus return the quote.)
func ( *Keys) () ( byte,  bool) {
	switch {
	case len(.buf) > 0:
		 = .buf[0]
		.buf = .buf[1:]
	case len(.macroKeys) > 0:
		 = byte(.macroKeys[0])
		.macroKeys = .macroKeys[1:]
	default:
		return byte(0), true
	}

	.matched = append(.matched, rune())

	return , false
}

// Caller returns the keys that have matched the command currently being ran.
func ( *Keys) () ( []rune) {
	return .matched
}

// Feed can be used to directly add keys to the stack.
// If begin is true, the keys are added on the top of
// the stack, otherwise they are being appended to it.
func ( *Keys) ( bool,  ...rune) {
	if len() == 0 {
		return
	}

	 := []rune(string())

	.mutex.Lock()
	defer .mutex.Unlock()

	if  {
		.macroKeys = append(, .macroKeys...)
	} else {
		.macroKeys = append(.macroKeys, ...)
	}
}

func ( *Keys) ( []byte) (,  []byte) {
	if !rxRcvCursorPos.Match() {
		return , 
	}

	 := rxRcvCursorPos.FindAll(, -1)
	 = [len()-1]
	 = rxRcvCursorPos.ReplaceAll(, nil)

	return
}