package completion

import (
	
	

	
	
	
)

const (
	trailingDescLen  = 3
	trailingValueLen = 4
)

var sanitizer = strings.NewReplacer(
	"\n", ``,
	"\r", ``,
	"\t", ``,
)

// prepare builds the list of completions, hint/usage messages
// and prefix/suffix strings, but does not attempt any candidate
// insertion/abortion on the line.
func ( *Engine) ( Values) {
	.prefix = ""
	.groups = make([]*group, 0)

	.setPrefix()
	.setSuffix()
	.generate()
}

func ( *Engine) ( Values) {
	// Compute hints once we found either any errors,
	// or if we have no completions but usage strings.
	defer func() {
		.hintCompletions()
	}()

	// Nothing else to do if no completions
	if len(.values) == 0 {
		return
	}

	// Apply the prefix to the completions, and filter out any
	// completions that don't match, optionally ignoring case.
	 := .config.GetBool("completion-ignore-case")
	.values = .values.FilterPrefix(.prefix, !)

	// Classify, group together and initialize completions.
	.values.EachTag(.generateGroup())
	.justifyGroups()
}

func ( *Engine) ( Values) {
	switch .PREFIX {
	case "":
		// Select the character just before the cursor.
		 := .cursor.Pos() - 1
		if  < 0 {
			 = 0
		}

		,  := .line.SelectBlankWord()

		// Safety checks and adjustments.
		if  >  {
			,  = , 
		}

		if  < .line.Len() {
			++
		}

		// You might wonder why we trim spaces here:
		// in practice we don't really ever want to
		// consider "how many spaces are somewhere".
		.prefix = strings.TrimSpace(string((*.line)[:]))

	default:
		.prefix = .PREFIX
	}
}

func ( *Engine) ( Values) {
	switch .SUFFIX {
	case "":
		 := .cursor.Pos()
		,  := .line.SelectBlankWord()

		// Safety checks and adjustments.
		if  < .line.Len() {
			++
		}

		if  <  {
			,  = , 
		}

		// Add back single or double quotes in the character after epos is one of them.
		if  < .line.Len() {
			if (*.line)[] == '\'' || (*.line)[] == '"' {
				++
			}
		}

		.suffix = strings.TrimSpace(string((*.line)[:]))

	default:
		.suffix = .SUFFIX
	}
}

// Returns a function to run on each completio group tag.
func ( *Engine) ( Values) func( string,  RawValues) {
	return func( string,  RawValues) {
		// Separate the completions that have a description and
		// those which don't, and devise if there are aliases.
		, ,  := .groupNonDescribed(&, )

		// Create a "first" group with the "first" grouped values
		.newCompletionGroup(, , , )

		// If we have a remaining group of values without descriptions,
		// we will print and use them in a separate, anonymous group.
		if len() > 0 {
			.newCompletionGroup(, "", , )
		}
	}
}

// groupNonDescribed separates values based on whether they have descriptions, or are aliases of each other.
func ( *Engine) ( *Values,  RawValues) (,  RawValues,  []string) {
	var  []string

	 := ""
	if .prefix != "\"\"" && .prefix != "''" {
		 = .prefix
	}

	for ,  := range  {
		// Ensure all values have a display string.
		if .Display == "" {
			.Display = .Value
		}

		// Currently this is because errors are passed as completions.
		if strings.HasPrefix(.Value, +"ERR") && .Value == +"_" {
			.Messages.Add(color.FgRed + .Display + .Description)

			continue
		}

		// Grid completions
		if .Description == "" {
			 = append(, )

			continue
		}

		 = append(, .Description)
		 = append(, )
	}

	// if no candidates have a description, swap
	if len() == 0 {
		 = 
		 = make(RawValues, 0)
	}

	return , , 
}

func ( *Engine) () ( *group) {
	for ,  := range .groups {
		if .isCurrent {
			return 
		}
	}
	// We might, for whatever reason, not find one.
	// If there are groups but no current, make first one the king.
	if len(.groups) > 0 {
		for ,  := range .groups {
			if len(.rows) > 0 {
				.isCurrent = true
				return 
			}
		}
	}

	return
}

// cycleNextGroup - Finds either the first non-empty group,
// or the next non-empty group after the current one.
func ( *Engine) () {
	for ,  := range .groups {
		if .isCurrent {
			.isCurrent = false

			if  == len(.groups)-1 {
				.groups[0].isCurrent = true
			} else {
				.groups[+1].isCurrent = true
			}

			break
		}
	}

	for {
		 := .currentGroup()
		if len(.rows) == 0 {
			.()
			continue
		}

		return
	}
}

// cyclePreviousGroup - Same as cycleNextGroup but reverse.
func ( *Engine) () {
	for ,  := range .groups {
		if .isCurrent {
			.isCurrent = false

			if  == 0 {
				.groups[len(.groups)-1].isCurrent = true
			} else {
				.groups[-1].isCurrent = true
			}

			break
		}
	}

	for {
		 := .currentGroup()
		if len(.rows) == 0 {
			.()
			continue
		}

		return
	}
}

func ( *Engine) ( Values) {
	 := make([]*group, 0)
	 := 0

	for ,  := range .groups {
		// Skip groups that are not to be justified
		 := .Pad[.tag]
		if ! {
			 = .Pad["*"]
		}

		if ! {
			continue
		}

		// Skip groups that are aliased or have more than one column
		if .aliased || len(.columnsWidth) > 1 {
			continue
		}

		// Else this group should be justified-padded globally.
		 = append(, )

		if .longestValue >  {
			 = .longestValue
		}
	}

	for ,  := range  {
		.columnsWidth[0] = 
		.longestValue = 
	}
}

func ( *Engine) (,  int) (int, int) {
	 := .currentGroup()

	 := .keys.Caller()
	 := string()

	if  > 0 {
		if .aliased &&  != term.ArrowRight &&  != term.ArrowDown {
			,  = 0, 
		} else if  == term.ArrowDown {
			,  = 0, 
		}
	} else {
		if .aliased &&  != term.ArrowLeft &&  != term.ArrowUp {
			,  = 0, 1*
		} else if  == term.ArrowUp {
			,  = 0, 1*
		}
	}

	return , 
}

// adjustSelectKeymap is only called when the selector function has been used.
func ( *Engine) () {
	if .keymap.Local() != keymap.Isearch {
		.keymap.SetLocal(keymap.MenuSelect)
	}
}

// completionCount returns the number of completions for a given group,
// as well as the number of real terminal lines it spans on, including
// the group name if there is one.
func ( *Engine) () ( int,  int) {
	for ,  := range .groups {
		// First, agree on the number of comps.
		for ,  := range .rows {
			 += len()
		}

		// One line for the group name
		if .tag != "" {
			++
		}

		if .maxY > len(.rows) {
			 += .maxY
		} else {
			 += len(.rows)
		}
	}

	return , 
}

func ( *Engine) () bool {
	switch len(.groups) {
	case 0:
		return false

	case 1:
		 := .currentGroup()
		if  == nil {
			return false
		}

		if len(.rows) == 1 {
			return len(.rows[0]) == 1
		}

		return len(.rows) == 1

	default:
		var  int

	:
		for ,  := range .groups {
			for ,  := range .rows {
				++
				for range  {
					++
				}
				if  > 1 {
					break 
				}
			}
		}

		return  == 1
	}
}

func ( *Engine) () bool {
	for ,  := range .groups {
		if len(.rows) > 0 {
			return false
		}
	}

	return true
}

func ( *Engine) (,  bool) {
	.selected = Candidate{}

	// Drop the list of already generated/prepared completion candidates.
	if  {
		.usedY = 0
		.groups = make([]*group, 0)
	}

	// Drop the completion generation function.
	if  {
		.cached = nil
	}

	// If generated choices were kept, select first choice.
	if len(.groups) > 0 {
		for ,  := range .groups {
			.isCurrent = false
		}

		.groups[0].isCurrent = true
	}
}

func ( *Engine) () bool {
	// Autocomplete is not needed when already completing,
	// or when the input line is empty (would always trigger)
	 := .config.GetBool("autocomplete") &&
		.keymap.Local() != keymap.MenuSelect &&
		.keymap.Local() != keymap.Isearch &&
		.line.Len() > 0

		// Not possible in Vim command mode either.
	 := .keymap.Main() != keymap.ViCommand &&
		.keymap.Local() != keymap.Isearch

	if  &&  && len(.selected.Value) == 0 {
		return true
	}

	if .keymap.Local() != keymap.MenuSelect && .autoForce {
		return true
	}

	return false
}

func ( *Engine) () int {
	var  int
	var  bool

	for ,  := range .groups {
		 := 0

		for ,  := range .rows {
			 += len()
		}

		if  == 0 {
			continue
		}

		if .tag != "" {
			++
		}

		if .isCurrent {
			 += .posY
			 = true

			break
		}

		 += .maxY
	}

	// If there was no current group, it means
	// we showed completions but there is no
	// candidate selected yet, return 0
	if ! {
		return 0
	}

	return 
}

func sum( []int) ( int) {
	for ,  := range  {
		 += 
	}

	return
}

func hasUpper( []rune) bool {
	for ,  := range  {
		if unicode.IsUpper() {
			return true
		}
	}

	return false
}

func longest( []string,  bool) int {
	var  int

	for ,  := range  {
		if  {
			 = color.Strip()
		}

		if len() >  {
			 = len()
		}
	}

	return 
}