package console

import (
	
	
	
	
	
	

	
	
)

// Start - Start the console application (readline loop). Blocking.
// The error returned will always be an error that the console
// application does not understand or cannot handle.
func ( *Console) () error {
	return .StartContext(context.Background())
}

// StartContext is like console.Start(). with a user-provided context.
func ( *Console) ( context.Context) error {
	.loadActiveHistories()

	// Print the console logo
	if .printLogo != nil {
		.printLogo()
	}

	 := "" // used to check if last read line is empty.

	for {
		.displayPostRun()

		// Always ensure we work with the active menu, with freshly
		// generated commands, bound prompts and some other things.
		 := .activeMenu()
		.resetPreRun()

		if  := .runAllE(.PreReadlineHooks);  != nil {
			.ErrorHandler(PreReadError{newError(, "Pre-read error")})

			continue
		}

		// Block and read user input.
		,  := .shell.Readline()

		.displayPostRun()

		if  != nil {
			.handleInterrupt()

			 = 

			continue
		}

		// Any call to the SwitchMenu() while we were reading user
		// input (through an interrupt handler) might have changed it,
		// so we must be sure we use the good one.
		 = .activeMenu()

		// Parse the line with bash-syntax, removing comments.
		,  := .parse()
		if  != nil {
			.ErrorHandler(ParseError{newError(, "Parsing error")})
			continue
		}

		if len() == 0 {
			 = 
			continue
		}

		// Run user-provided pre-run line hooks,
		// which may modify the input line args.
		,  = .runLineHooks()
		if  != nil {
			.ErrorHandler(LineHookError{newError(, "Line error")})
			continue
		}

		// Run all pre-run hooks and the command itself
		// Don't check the error: if its a cobra error,
		// the library user is responsible for setting
		// the cobra behavior.
		// If it's an interrupt, we take care of it.
		if  := .execute(, , , false);  != nil {
			.ErrorHandler(ExecutionError{newError(, "")})
		}

		 = 
	}
}

// RunCommandArgs is a convenience function to run a command line in a given menu.
// After running, the menu's commands are reset, and the prompts reloaded, therefore
// mostly mimicking the behavior that is the one of the normal readline/run/readline
// workflow.
// Although state segregation is a priority for this library to be ensured as much
// as possible, you should be cautious when using this function to run commands.
func ( *Menu) ( context.Context,  []string) ( error) {
	// The menu used and reset is the active menu.
	// Prepare its output buffer for the command.
	.resetPreRun()

	// Run the command and associated helpers.
	return .console.execute(, , , !.console.isExecuting)
}

// RunCommandLine is the equivalent of menu.RunCommandArgs(), but accepts
// an unsplit command line to execute. This line is split and processed in
// *sh-compliant form, identically to how lines are in normal console usage.
func ( *Menu) ( context.Context,  string) ( error) {
	if len() == 0 {
		return
	}

	// Split the line into shell words.
	,  := shellquote.Split()
	if  != nil {
		return fmt.Errorf("line error: %w", )
	}

	return .RunCommandArgs(, )
}

// execute - The user has entered a command input line, the arguments have been processed:
// we synchronize a few elements of the console, then pass these arguments to the command
// parser for execution and error handling.
// Our main object of interest is the menu's root command, and we explicitly use this reference
// instead of the menu itself, because if RunCommand() is asynchronously triggered while another
// command is running, the menu's root command will be overwritten.
func ( *Console) ( context.Context,  *Menu,  []string,  bool) error {
	if ! {
		.mutex.RLock()
		.isExecuting = true
		.mutex.RUnlock()
	}

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

	// Our root command of interest, used throughout this function.
	 := .Command

	// Find the target command: if this command is filtered, don't run it.
	, ,  := .Find()

	if  := .CheckIsAvailable();  != nil {
		return 
	}

	// Reset all flags to their default values.
	resetFlagsDefaults()

	// Console-wide pre-run hooks, cannot.
	if  := .runAllE(.PreCmdRunHooks);  != nil {
		return fmt.Errorf("pre-run error: %s", .Error())
	}

	// Assign those arguments to our parser.
	.SetArgs()

	// The command execution should happen in a separate goroutine,
	// and should notify the main goroutine when it is done.
	,  := context.WithCancelCause()

	.SetContext()

	// Start monitoring keyboard and OS signals.
	 := .monitorSignals()

	// And start the command execution.
	go .executeCommand(, )

	// Wait for the command to finish, or for an OS signal to be caught.
	select {
	case <-.Done():
		 := context.Cause()

		if !errors.Is(, context.Canceled) {
			return 
		}

	case  := <-:
		(errors.New(.String()))

		.handleInterrupt(errors.New(.String()))
	}

	return nil
}

// Run the command in a separate goroutine, and cancel the context when done.
func ( *Console) ( *cobra.Command,  context.CancelCauseFunc) {
	if  := .Execute();  != nil {
		()

		return
	}

	// And the post-run hooks in the same goroutine,
	// because they should not be skipped even if
	// the command is backgrounded by the user.
	if  := .runAllE(.PostCmdRunHooks);  != nil {
		()
		return
	}

	// Command successfully executed, cancel the context.
	(nil)
}

func ( *Console) () {
	.shell.History.Delete()

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

func ( *Console) ( []func() error) error {
	for ,  := range  {
		if  := ();  != nil {
			return 
		}
	}

	return nil
}

func ( *Console) ( []string) ([]string, error) {
	 := 

	// Or modify them again
	for ,  := range .PreCmdRunLineHooks {
		var  error

		if ,  = ();  != nil {
			return nil, 
		}
	}

	return , nil
}

func ( *Console) ( string) {
	if .NewlineBefore {
		if !.NewlineWhenEmpty {
			if !.lineEmpty() {
				fmt.Println()
			}
		} else {
			fmt.Println()
		}
	}
}

func ( *Console) ( string) {
	if .NewlineAfter {
		if !.NewlineWhenEmpty {
			if !.lineEmpty() {
				fmt.Println()
			}
		} else {
			fmt.Println()
		}
	}

	.printed = false
}

// monitorSignals - Monitor the signals that can be sent to the process
// while a command is running. We want to be able to cancel the command.
func ( *Console) () <-chan os.Signal {
	 := make(chan os.Signal, 1)

	signal.Notify(
		,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT,
		// syscall.SIGKILL,
	)

	return 
}