package console

import (
	
	
	

	

	
	
	
)

// Console is an integrated console application instance.
type Console struct {
	// Application
	name          string           // Used in the prompt, and for readline `.inputrc` application-specific settings.
	shell         *readline.Shell  // Provides readline functionality (inputs, completions, hints, history)
	printLogo     func(c *Console) // Simple logo printer.
	cmdHighlight  string           // Ansi code for highlighting of command in default highlighter. Green by default.
	flagHighlight string           // Ansi code for highlighting of flag in default highlighter. Grey by default.
	menus         map[string]*Menu // Different command trees, prompt engines, etc.
	filters       []string         // Hide commands based on their attributes and current context.
	isExecuting   bool             // Used by log functions, which need to adapt behavior (print the prompt, etc.)
	printed       bool             // Used to adjust asynchronous messages too.
	mutex         *sync.RWMutex    // Concurrency management.

	// Execution

	// Leave an empty line before executing the command.
	NewlineBefore bool

	// Leave an empty line after executing the command.
	// Note that if you also want this newline to be used when logging messages
	// with TransientPrintf(), Printf() calls, you should leave this to false,
	// and add a leading newline to your prompt instead: the readline shell will
	// know how to handle it in all situations.
	NewlineAfter bool

	// Leave empty lines with NewlineBefore and NewlineAfter, even if the provided input was empty.
	// Empty characters are defined as any number of spaces and tabs. The 'empty' character set
	// can be changed by modifying Console.EmptyChars
	// This field is false by default.
	NewlineWhenEmpty bool

	// Characters that are used to determine whether an input line was empty. If a line is not entirely
	// made up by any of these characters, then it is not considered empty. The default characters
	// are ' ' and '\t'.
	EmptyChars []rune

	// PreReadlineHooks - All the functions in this list will be executed,
	// in their respective orders, before the console starts reading
	// any user input (ie, before redrawing the prompt).
	PreReadlineHooks []func() error

	// PreCmdRunLineHooks - Same as PreCmdRunHooks, but will have an effect on the
	// input line being ultimately provided to the command parser. This might
	// be used by people who want to apply supplemental, specific processing
	// on the command input line.
	PreCmdRunLineHooks []func(args []string) ([]string, error)

	// PreCmdRunHooks - Once the user has entered a command, but before executing
	// the target command, the console will execute every function in this list.
	// These hooks are distinct from the cobra.PreRun() or OnInitialize hooks,
	// and might be used in combination with them.
	PreCmdRunHooks []func() error

	// PostCmdRunHooks are run after the target cobra command has been executed.
	// These hooks are distinct from the cobra.PreRun() or OnFinalize hooks,
	// and might be used in combination with them.
	PostCmdRunHooks []func() error
}

// New - Instantiates a new console application, with sane but powerful defaults.
// This instance can then be passed around and used to bind commands, setup additional
// things, print asynchronous messages, or modify various operating parameters on the fly.
// The app parameter is an optional name of the application using this console.
func ( string) *Console {
	 := &Console{
		name:  ,
		shell: readline.NewShell(inputrc.WithApp(strings.ToLower())),
		menus: make(map[string]*Menu),
		mutex: &sync.RWMutex{},
	}

	// Quality of life improvements.
	.setupShell()

	// Make a default menu and make it current.
	// Each menu is created with a default prompt engine.
	 := .NewMenu("")
	.active = true

	// Set the history for this menu
	for ,  := range .historyNames {
		.shell.History.Add(, .histories[])
	}

	// Syntax highlighting, multiline callbacks, etc.
	.cmdHighlight = line.GreenFG 
	.flagHighlight = line.BrightWhiteFG 
	.shell.AcceptMultiline = line.AcceptMultiline
	.shell.SyntaxHighlighter = .highlightSyntax 

	// Completion
	.shell.Completer = .complete
	completion.DefaultStyleConfig()

	// Defaults
	.EmptyChars = []rune{' ', '\t'}

	return 
}

// Shell returns the console readline shell instance, so that the user can
// further configure it or use some of its API for lower-level stuff.
func ( *Console) () *readline.Shell {
	return .shell
}


//
// Settings & Initialisation Functions ------------------------------------------------------------- //
//

// SetPrintLogo - Sets the function that will be called to print the logo.
func ( *Console) ( func( *Console)) {
	.printLogo = 
}

// SetDefaultCommandHighlight allows the user to change the highlight color for 
// a command in the default syntax highlighter using an ansi code.
// This action has no effect if a custom syntax highlighter for the shell is set.
// By default, the highlight code is green ("\x1b[32m").
func ( *Console) ( string) {
	.cmdHighlight = 
}

// SetDefaultFlagHighlight allows the user to change the highlight color for 
// a flag in the default syntax highlighter using an ansi color code.
// This action has no effect if a custom syntax highlighter for the shell is set.
// By default, the highlight code is grey ("\x1b[38;05;244m").
func ( *Console) ( string) {
	.flagHighlight = 
}

//
// Menu Management --------------------------------------------------------------------------------- //
//

// NewMenu - Create a new command menu, to which the user
// can attach any number of commands (with any nesting), as
// well as some specific items like history sources, prompt
// configurations, sets of expanded variables, and others.
func ( *Console) ( string) *Menu {
	.mutex.RLock()
	defer .mutex.RUnlock()
	 := newMenu(, )
	.menus[] = 

	return 
}

// ActiveMenu - Return the currently used console menu.
func ( *Console) () *Menu {
	.mutex.Lock()
	defer .mutex.Unlock()

	return .activeMenu()
}

// Menu returns one of the console menus by name, or nil if no menu is found.
func ( *Console) ( string) *Menu {
	.mutex.Lock()
	defer .mutex.Unlock()

	return .menus[]
}

// SwitchMenu - Given a name, the console switches its command menu:
// The next time the console rebinds all of its commands, it will only bind those
// that belong to this new menu. If the menu is invalid, i.e that no commands
// are bound to this menu name, the current menu is kept.
func ( *Console) ( string) {
	.mutex.Lock()
	,  := .menus[]
	.mutex.Unlock()

	if  &&  != nil {
		// Only switch if the target menu was found.
		 := .activeMenu()
		if  != nil &&  ==  {
			return
		}

		if  != nil {
			.active = false
		}

		.active = true

		// Remove the currently bound history sources
		// (old menu) and bind the ones peculiar to this one.
		.shell.History.Delete()

		for ,  := range .historyNames {
			.shell.History.Add(, .histories[])
		}

		// Regenerate the commands, outputs and everything related.
		.resetPreRun()
	}
}

//
// Message Display Functions ----------------------------------------------------------------------- //
//

// TransientPrintf prints a string message (a log, or more broadly, an asynchronous event)
// without bothering the user, displaying the message and "pushing" the prompt below it.
// The message is printed regardless of the current menu.
//
// If this function is called while a command is running, the console will simply print the log
// below the line, and will not print the prompt. In any other case this function works normally.
func ( *Console) ( string,  ...any) ( int,  error) {
	if .isExecuting {
		return fmt.Printf(, ...)
	}

	// If the last message we printed asynchronously
	// immediately precedes this new message, move up
	// another row, so we don't waste too much space.
	if .printed && .NewlineAfter {
		fmt.Print("\x1b[1A")
	}

	if .NewlineAfter {
		 += "\n"
	}

	.printed = true

	return .shell.PrintTransientf(, ...)
}

// Printf prints a string message (a log, or more broadly, an asynchronous event)
// below the current prompt. The message is printed regardless of the current menu.
//
// If this function is called while a command is running, the console will simply print the log
// below the line, and will not print the prompt. In any other case this function works normally.
func ( *Console) ( string,  ...any) ( int,  error) {
	if .isExecuting {
		return fmt.Printf(, ...)
	}

	return .shell.Printf(, ...)
}

//
// Other Utility Functions ------------------------------------------------------------------------- //
//

// SystemEditor - This function is a renamed-reexport of the underlying readline.StartEditorWithBuffer
// function, which enables you to conveniently edit files/buffers from within the console application.
// Naturally, the function will block until the editor is exited, and the updated buffer is returned.
// The filename parameter can be used to pass a specific filename.ext pattern, which might be useful
// if the editor has builtin filetype plugin functionality.
func ( *Console) ( []byte,  string) ([]byte, error) {
	 := .shell.Config.GetString("editing-mode") == "emacs"

	,  := .shell.Buffers.EditBuffer([]rune(string()), "", , )

	return []byte(string()), 
}

func ( *Console) () {
	// Some options should be set to on because they
	// are quite neceessary for efficient console use.
	 := .shell.Config

	// Input line
	.Set("autopairs", true)
	.Set("blink-matching-paren", true)

	// Completion
	.Set("completion-ignore-case", true)
	.Set("skip-completed-text", true)
	.Set("menu-complete-display-prefix", true)

	// General UI
	.Set("usage-hint-always", true)
	.Set("history-autosuggest", true)
}

func ( *Console) () *Menu {
	for ,  := range .menus {
		if .active {
			return 
		}
	}

	// Else return the default menu.
	return .menus[""]
}