package macro

import (
	
	
	

	
	
	
	
)

// validMacroKeys - All valid macro IDs (keys) for read/write Vim registers.
var validMacroKeys = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\""

// Engine manages all things related to keyboard macros:
// recording, dumping and feeding (running) them to the shell.
type Engine struct {
	recording  bool
	current    []rune          // Key sequence of the current macro being recorded.
	currentKey rune            // The identifier of the macro being recorded.
	macros     map[rune]string // All previously recorded macros.
	started    bool

	keys   *core.Keys // The engine feeds macros directly in the key stack.
	hint   *ui.Hint   // The engine notifies when macro recording starts/stops.
	status string     // The hint status displaying the currently recorded macro.
}

// NewEngine is a required constructor to setup a working macro engine.
func ( *core.Keys,  *ui.Hint) *Engine {
	return &Engine{
		current: make([]rune, 0),
		macros:  make(map[rune]string),
		keys:    ,
		hint:    ,
	}
}

// RecordKeys is being passed every key read by the shell, and will save
// those entered while the engine is in record mode. All others are ignored.
func ( *Engine) {
	if !.recording {
		return
	}

	 := core.MacroKeys(.keys)
	if len() == 0 {
		return
	}

	// The first call to record should not add
	// the caller keys that started the recording.
	if !.started {
		.current = append(.current, ...)
	}

	.started = false

	.hint.Persist(.status + inputrc.EscapeMacro(string(.current)) + color.Reset)
}

// StartRecord starts saving all user key input to record a macro.
// If the key parameter is an alphanumeric character, the macro recorded will be
// stored and used through this letter argument, just like macros work in Vim.
// If the key is neither valid nor the null value, the engine does not start.
// A notification containing the saved sequence is given through the hint section.
func ( *Engine) ( rune) {
	switch {
	case isValidMacroID(),  == 0:
		.currentKey = 
	default:
		return
	}

	.started = true
	.recording = true
	.status = color.Dim + "Recording macro: " + color.Bold
	.hint.Persist(.status)
}

// StopRecord stops using key input as part of a macro.
// The hint section displaying the currently saved sequence is cleared.
func ( *Engine) ( ...rune) {
	.recording = false

	// Remove the hint.
	.hint.ResetPersist()

	if len(.current) == 0 {
		return
	}

	.current = append(.current, ...)
	 := inputrc.EscapeMacro(string(.current))

	.macros[.currentKey] = 
	.macros[rune(0)] = 

	.current = make([]rune, 0)
}

// Recording returns true if the macro engine is recording the keys for a macro.
func ( *Engine) () bool {
	return .recording
}

// RunLastMacro feeds keys the last recorded macro to the shell's key stack,
// so that the macro is replayed.
// Note that this function only feeds the keys of the macro back into the key
// stack: it does not dispatch them to commands, therefore not running any.
func ( *Engine) () {
	if len(.macros) == 0 {
		return
	}

	 := inputrc.Unescape(.macros[rune(0)])

	if len() == 0 {
		return
	}

	.keys.Feed(false, []rune()...)
}

// RunMacro runs a given macro, injecting its key sequence back into the shell key stack.
// The key argument should either be one of the valid alphanumeric macro identifiers, or
// a nil rune (in which case the last recorded macro is ran).
// Note that this function only feeds the keys of the macro back into the key
// stack: it does not dispatch them to commands, therefore not running any.
func ( *Engine) ( rune) {
	if !isValidMacroID() &&  != 0 {
		return
	}

	 := .macros[]
	if len() == 0 {
		return
	}

	 = strings.ReplaceAll(, `\e`, "\x1b")
	.keys.Feed(false, []rune()...)
}

// PrintLastMacro dumps the last recorded macro sequence to the screen.
func ( *Engine) () {
	if len(.macros) == 0 {
		return
	}

	// Print the macro and the prompt.
	// The shell takes care of clearing itself
	// before printing, and refreshing after.
	fmt.Printf("\n%s\n", .macros[.currentKey])
}

// PrintAllMacros dumps all macros to the screen, which one line
// per saved macro sequence, next to its corresponding key ID.
func ( *Engine) () {
	var  []rune

	for  := range .macros {
		 = append(, )
	}

	sort.Slice(, func(,  int) bool {
		return [] < []
	})

	for ,  := range  {
		 := .macros[]
		if  == "" {
			continue
		}

		if  == 0 {
			 = '"'
		}

		fmt.Printf("\"%s\": %s\n", string(), )
	}
}

func isValidMacroID( rune) bool {
	for ,  := range validMacroKeys {
		if  ==  {
			return true
		}
	}

	return false
}