package ui

import (
	
	
	

	
	
	
	
	
)

const (
	secondaryPromptDefault = "\x1b[1;30m\U00002514 \x1b[0m"
	multilineColumnDefault = "\x1b[1;30m\U00002502 \x1b[0m"
)

// Prompt stores all prompt rendering/generation functions and is
// in charge of displaying them, as well as computing their offsets.
type Prompt struct {
	primaryF    func() string
	primaryRows int
	primaryCols int

	secondaryF func() string
	transientF func() string
	rightF     func() string
	tooltipF   func() string

	// True if some logs have printed asynchronously
	// since last loop. Check refresh prompt funcs.
	refreshing bool

	// Shell parameters
	line    *core.Line
	cursor  *core.Cursor
	keymaps *keymap.Engine
	opts    *inputrc.Config
}

// NewPrompt is a required constructor to initialize the prompt system.
func ( *core.Line,  *core.Cursor,  *keymap.Engine,  *inputrc.Config) *Prompt {
	return &Prompt{
		line:    ,
		cursor:  ,
		keymaps: ,
		opts:    ,
	}
}

// Primary uses a function returning the string to use as the primary prompt.
func ( *Prompt) ( func() string) {
	.primaryF = 
}

// Right uses a function returning the string to use as the right prompt.
func ( *Prompt) ( func() string) {
	.rightF = 
}

// Secondary uses a function returning the prompt to use as the secondary prompt.
func ( *Prompt) ( func() string) {
	.secondaryF = 
}

// Transient uses a function returning the prompt to use as a transient prompt.
func ( *Prompt) ( func() string) {
	.transientF = 
}

// Tooltip uses a function returning the prompt to use as a tooltip prompt.
func ( *Prompt) ( func( string) string) {
	if  == nil {
		return
	}

	// Wrap the user-provided function into a callback using out input line.
	.tooltipF = func() string {
		var  string

		 := strings.Split(string(*.line), " ")

		if len() > 0 {
			 = [0]
		}

		return ()
	}
}

// PrimaryPrint prints the primary prompt string, excluding
// the last line if the primary prompt spans on several lines.
func ( *Prompt) () {
	.refreshing = false

	if .primaryF == nil {
		return
	}

	 := .primaryF()

	,  := .formatPrimaryLines()

	// Format the last line with the editing status.
	 = .formatLastPrompt()

	// Print the various lines.
	if  != "" {
		fmt.Print()
	}

	fmt.Print()

	// And compute coordinates
	.primaryRows = strings.Count(, "\n")
	.primaryCols = strutil.RealLength()

	if .primaryCols > 0 {
		.primaryCols--
	}
}

// PrimaryUsed returns the number of terminal rows on which
// the primary prompt string spans, excluding the last line
// if it contains newlines.
func ( *Prompt) () int {
	return .primaryRows
}

// LastPrint prints the last line of the primary prompt, if the latter
// spans on several lines. If not, this function will actually print
// the entire primary prompt, and PrimaryPrint() will not print anything.
func ( *Prompt) () {
	if .primaryF == nil {
		return
	}

	// Only display the last line, but overwrite the number of
	// rows used since any redisplay of all lines but the last
	// will trigger their  own recomputation.
	 := strings.Split(.primaryF(), "\n")

	// Print the prompt and compute columns.
	if len() == 0 {
		return
	}

	 := .formatLastPrompt([len()-1])

	fmt.Print()

	.primaryCols = strutil.RealLength()
	if .primaryCols > 0 {
		.primaryCols--
	}
}

// LastUsed returns the number of terminal columns used by the last
// part of the primary prompt (of the entire string if not multiline).
// This, in effect, returns the X coordinate at which the input line
// should be printed, and indentation for subsequent lines if several.
func ( *Prompt) () int {
	if .primaryF == nil {
		return 0
	}

	// Only display the last line, but overwrite the number of
	// rows used since any redisplay of all lines but the last
	// will trigger their  own recomputation.
	 := strings.Split(.primaryF(), "\n")
	if len() == 0 {
		return 0
	}

	 := .formatLastPrompt([len()-1])
	.primaryCols = strutil.RealLength()

	if .primaryCols > 0 {
		.primaryCols--
	}

	return .primaryCols
}

// SecondaryPrint prints the last cursor in secondary prompt mode,
// which is always activated when the current input line is a multiline one.
func ( *Prompt) () {
	if .secondaryF != nil {
		fmt.Print(.secondaryF())
		return
	}

	fmt.Print(secondaryPromptDefault)
}

// MultilineColumnPrint prints the multiline editor column status indicator.
// It either prints a default, numbered or user-defined column.
func ( *Prompt) () {
	 := .opts.GetBool("multiline-column-numbered")
	 := .opts.GetString("multiline-column-custom")
	 := .opts.GetBool("multiline-column")

	switch {
	case :
		 := ""
		for  := range .line.Lines() {
			 += fmt.Sprintf("\n\x1b[1;30m%d\x1b[0m", +2)
		}

		fmt.Print()

	case len() > 0:
		 := ""
		for range .line.Lines() {
			 += fmt.Sprintf("\n%s\x1b[0m", )
		}

		fmt.Print()

	case :
		 := ""
		for range .line.Lines() {
			 += "\n" + multilineColumnDefault
		}

		fmt.Print()
	}
}

// RightPrint prints the right-sided prompt strings, which might be either
// a traditional RPROMPT string, or a tooltip prompt if any must be rendered.
// If force is true, whatever rprompt or tooltip exists will be printed.
// If false, only the rprompt, if it exists, will be printed.
func ( *Prompt) ( int,  bool) {
	var  string

	if .tooltipF != nil &&  {
		 = .tooltipF()
	}

	if  == "" && .rightF != nil {
		 = .rightF()
	}

	if  == "" {
		return
	}

	if ,  := .formatRightPrompt(, );  {
		fmt.Print()
	} else {
		fmt.Print(term.ClearLineAfter)
	}
}

// TransientPrint prints the transient prompt.
func ( *Prompt) () {
	if .transientF == nil {
		return
	}

	// Clean everything below where the prompt will be printed.
	term.MoveCursorBackwards(term.GetWidth())
	term.MoveCursorUp(.primaryRows)
	fmt.Print(term.ClearScreenBelow)

	// And print the prompt
	fmt.Print(.transientF())
}

// Refreshing returns true if the prompt is currently redisplaying
// itself (at least the primary prompt), or false if not.
func ( *Prompt) () bool {
	return .refreshing
}

func ( *Prompt) ( string) string {
	if !.opts.GetBool("show-mode-in-prompt") {
		return 
	}

	var  string

	switch {
	case .keymaps.IsEmacs():
		 = .opts.GetString("emacs-mode-string")
	case .keymaps.Main() == keymap.ViCommand:
		 = .opts.GetString("vi-cmd-mode-string")
	case .keymaps.Main() == keymap.ViInsert:
		 = .opts.GetString("vi-ins-mode-string")
	}

	// Fix parsing of inputrc which sometimes preserves quotes on some
	// values, and remove bash readline begin/end non-printable delimiters.
	 = strings.Trim(, "\"")

	 := regexp.MustCompile(`\\1`)
	 := regexp.MustCompile(`\\2`)

	// Remove delimiters, and replace quoted escape sequences
	 = .ReplaceAllString(, "")
	 = .ReplaceAllString(, "")
	 = strings.ReplaceAll(, "\\e", "\x1b")

	return  + 
}

func ( *Prompt) ( string,  int) ( string,  bool) {
	// Dimensions
	 := term.GetWidth()
	 := strutil.RealLength()
	 :=  -  - 

	// Adjust padding when the last line is as large as terminal.
	if  ==  {
		 =  - 
	}

	// Check that we have room for a right/tooltip prompt.
	 = (+ < ) ||  == 
	if  {
		 = fmt.Sprintf("%s%s", strings.Repeat(" ", ), )
	}

	return
}

func ( *Prompt) ( string) (,  string) {
	// Get all the lines but the last.
	 := strings.Split(, "\n")

	if len() > 1 {
		 = strings.Join([:len()-1], "\n") + "\n"
		 = [len()-1]
	} else {
		,  = , ""
	}

	return , 
}