package console

import (
	
	
	
	
	

	

	
	
	
)

// Prompt - A prompt is a set of functions that return the strings to print
// for each prompt type. The console will call these functions to retrieve
// the prompt strings to print. Each menu has its own prompt.
type Prompt = ui.Prompt

// Menu - A menu is a simple way to seggregate commands based on
// the environment to which they belong. For instance, when using a menu
// specific to some host/user, or domain of activity, commands will vary.
type Menu struct {
	name    string
	active  bool
	prompt  *Prompt
	console *Console

	// Maps interrupt signals (CtrlC/IOF, etc) to specific error handlers.
	interruptHandlers map[error]func(c *Console)

	// ErrorHandler is called when an error is encountered.
	//
	// If not set, the error is printed to the console on os.Stderr.
	ErrorHandler ErrorHandler

	// Input/output channels
	out *bytes.Buffer

	// The root cobra command/parser is the one returned by the handler provided
	// through the `menu.SetCommands()` function. This command is thus renewed after
	// each command invocation/execution.
	// You can still use it as you want, for instance to introspect the current command
	// state of your menu.
	*cobra.Command

	// Command spawner
	cmds Commands

	// An error template to use to produce errors when a command is unavailable.
	errFilteredTemplate string

	// History sources peculiar to this menu.
	historyNames []string
	histories    map[string]readline.History

	// Concurrency management
	mutex *sync.RWMutex
}

func newMenu( string,  *Console) *Menu {
	 := &Menu{
		console:           ,
		name:              ,
		Command:           &cobra.Command{},
		out:               bytes.NewBuffer(nil),
		interruptHandlers: make(map[error]func( *Console)),
		histories:         make(map[string]readline.History),
		mutex:             &sync.RWMutex{},
		ErrorHandler:      defaultErrorHandler,
	}

    // Prompt setup
     := (ui.NewPrompt(.name, , .out))
	.prompt = (*Prompt)()

	// Add a default in memory history to each menu
	// This source is dropped if another source is added
	// to the menu via `AddHistorySource()`.
	 := .defaultHistoryName()
	 := readline.NewInMemoryHistory()

	.historyNames = append(.historyNames, )
	.histories[] = 

	return 
}

// Name returns the name of this menu.
func ( *Menu) () string {
	return .name
}

// Prompt returns the prompt object for this menu.
func ( *Menu) () *Prompt {
	return .prompt
}

// AddHistorySource adds a source of history commands that will
// be accessible to the shell when the menu is active.
func ( *Menu) ( string,  readline.History) {
	.mutex.RLock()
	defer .mutex.RUnlock()

	if len(.histories) == 1 && .historyNames[0] == .defaultHistoryName() {
		delete(.histories, .defaultHistoryName())
		.historyNames = make([]string, 0)
	}

	.historyNames = append(.historyNames, )
	.histories[] = 
}

// AddHistorySourceFile adds a new source of history populated from and writing
// to the specified "filepath" parameter. On the first call to this function,
// the default in-memory history source is removed.
func ( *Menu) ( string,  string) {
	.mutex.RLock()
	defer .mutex.RUnlock()

	if len(.histories) == 1 && .historyNames[0] == .defaultHistoryName() {
		delete(.histories, .defaultHistoryName())
		.historyNames = make([]string, 0)
	}

	.historyNames = append(.historyNames, )
	.histories[], _ = readline.NewHistoryFromFile()
}

// DeleteHistorySource removes a history source from the menu.
// This normally should only be used in two cases:
// - You want to replace the default in-memory history with another one.
// - You want to replace one of your history sources for some reason.
func ( *Menu) ( string) {
	if  == .Name() {
		if  != "" {
			 = " (" +  + ")"
		}

		 = "local history" + 
	}

	delete(.histories, )

	for ,  := range .historyNames {
		if  ==  {
			.historyNames = append(.historyNames[:], .historyNames[+1:]...)

			break
		}
	}
}

// TransientPrintf prints a message to the console, but only if the current
// menu is active. If the menu is not active, the message is buffered and will
// be printed the next time the menu is active.
//
// The message is printed as a transient message, meaning that it will be
// printed above the current prompt, effectively "pushing" the prompt down.
//
// If this function is called while a command is running, the console
// will simply print the log below the current line, and will not print
// the prompt. In any other case this function will work normally.
func ( *Menu) ( string,  ...any) ( int,  error) {
	,  = fmt.Fprintf(.out, , ...)
	if  != nil {
		return
	}

	if !.active {
		fmt.Fprintf(.out, "\n")
		return
	}

	 := .out.String()
	.out.Reset()

	return .console.TransientPrintf("%s", )
}

// Printf prints a message to the console, but only if the current menu
// is active. If the menu is not active, the message is buffered and will
// be printed the next time the menu is active.
//
// Unlike TransientPrintf, this function will not print the message above
// the current prompt, but will instead print it below it.
//
// If this function is called while a command is running, the console
// will simply print the log below the current line, and will not print
// the prompt. In any other case this function will work normally.
func ( *Menu) ( string,  ...any) ( int,  error) {
	,  = fmt.Fprintf(.out, , ...)
	if  != nil {
		return
	}

	if !.active {
		fmt.Fprintf(.out, "\n")
		return
	}

	 := .out.String()
	.out.Reset()

	return .console.Printf("%s", )
}

// CheckIsAvailable checks if a target command is marked as filtered
// by the console application registered/and or active filters (added
// with console.Hide/ShowCommand()).
// If filtered, returns a template-formatted error message showing the
// list of incompatible filters. If not filtered, no error is returned.
func ( *Menu) ( *cobra.Command) error {
	if  == nil {
		return nil
	}

	 := .ActiveFiltersFor()
	if len() == 0 {
		return nil
	}

     := .errorFilteredCommandTemplate()

	var  strings.Builder


	 := strutil.Template(&, , map[string]interface{}{
		"menu":    ,
		"cmd":     ,
		"filters": ,
	})
	if  != nil {
		return 
	}

	return errors.New(.String())
}

// ActiveFiltersFor returns all the active menu filters that a given command
// does not declare as compliant with (added with console.Hide/ShowCommand()).
func ( *Menu) ( *cobra.Command) []string {
	if .Annotations == nil {
		if .HasParent() {
			return .(.Parent())
		}

		return nil
	}

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

	// Get the filters on the command
	 := .Annotations[CommandFilterKey]
	var  []string

	for ,  := range strings.Split(, ",") {
		for ,  := range .console.filters {
			if  != "" &&  ==  {
				 = append(, )
			}
		}
	}

	if len() > 0 || !.HasParent() {
		return 
	}

	// Any parent that is hidden make its whole subtree hidden also.
	return .(.Parent())
}

// SetErrFilteredCommandTemplate sets the error template to be used
// when a called command can't be executed because it's mark filtered.
func ( *Menu) ( string) {
	.errFilteredTemplate = 
}

// resetPreRun is called before each new read line loop and before arbitrary RunCommand() calls.
// This function is responsible for resetting the menu state to a clean state, regenerating the
// menu commands, and ensuring that the correct prompt is bound to the shell.
func ( *Menu) () {
	.mutex.Lock()
	defer .mutex.Unlock()

	// Commands
	if .cmds != nil {
		.Command = .cmds()
	}

	if .Command == nil {
		.Command = &cobra.Command{
			Annotations: make(map[string]string),
		}
	}

	// Hide commands that are not available
	.hideFilteredCommands(.Command)

    // Reset or adjust any buffered command output.
	.resetCmdOutput()             

    // Prompt binding
     := (*ui.Prompt)(.Prompt())
	ui.BindPrompt(, .console.shell) 
}

// hide commands that are filtered so that they are not
// shown in the help strings or proposed as completions.
func ( *Menu) ( *cobra.Command) {
	for ,  := range .Commands() {
		// Don't override commands if they are already hidden
		if .Hidden {
			continue
		}

		if  := .ActiveFiltersFor(); len() > 0 {
			.Hidden = true
		}
	}
}

func ( *Menu) () {
	 := strings.TrimSpace(.out.String())

	// If our command has printed everything to stdout, nothing to do.
	if len() == 0 ||  == "" {
		.out.Reset()
		return
	}

	// Add two newlines to the end of the buffer, so that the
	// next command will be printed slightly below the current one.
	.out.WriteString("\n")
}

func ( *Menu) () string {
	var  string

	if .name != "" {
		 = " (" + .name + ")"
	}

	return "local history" + 
}

func ( *Menu) ( []string) string {
	if .errFilteredTemplate != "" {
		return .errFilteredTemplate
	}

	return `Command {{.cmd.Name}} is only available for: {{range .filters }}
    - {{.}} {{end}}`
}