package edit

import (
	
	
	
	

	
	
	
	
	
)

//elvdoc:fn complete-getopt
//
// ```elvish
// edit:complete-getopt $args $opt-specs $arg-handlers
// ```
// Produces completions according to a specification of accepted command-line
// options (both short and long options are handled), positional handler
// functions for each command position, and the current arguments in the command
// line. The arguments are as follows:
//
// * `$args` is an array containing the current arguments in the command line
//   (without the command itself). These are the arguments as passed to the
//   [Argument Completer](#argument-completer) function.
//
// * `$opt-specs` is an array of maps, each one containing the definition of
//   one possible command-line option. Matching options will be provided as
//   completions when the last element of `$args` starts with a dash, but not
//   otherwise. Each map can contain the following keys (at least one of `short`
//   or `long` needs to be specified):
//
//   - `short` contains the one-letter short option, if any, without the dash.
//
//   - `long` contains the long option name, if any, without the initial two
//     dashes.
//
//   - `arg-optional`, if set to `$true`, specifies that the option receives an
//     optional argument.
//
//   - `arg-required`, if set to `$true`, specifies that the option receives a
//     mandatory argument. Only one of `arg-optional` or `arg-required` can be
//     set to `$true`.
//
//   - `desc` can be set to a human-readable description of the option which
//     will be displayed in the completion menu.
//
//   - `completer` can be set to a function to generate possible completions for
//     the option argument. The function receives as argument the element at
//     that position and return zero or more candidates.
//
// * `$arg-handlers` is an array of functions, each one returning the possible
//   completions for that position in the arguments. Each function receives
//   as argument the last element of `$args`, and should return zero or more
//   possible values for the completions at that point. The returned values can
//   be plain strings or the output of `edit:complex-candidate`. If the last
//   element of the list is the string `...`, then the last handler is reused
//   for all following arguments.
//
// Example:
//
// ```elvish-transcript
// ~> fn complete [@args]{
//      opt-specs = [ [&short=a &long=all &desc="Show all"]
//                    [&short=n &desc="Set name" &arg-required=$true
//                     &completer= [_]{ put name1 name2 }] ]
//      arg-handlers = [ [_]{ put first1 first2 }
//                       [_]{ put second1 second2 } ... ]
//      edit:complete-getopt $args $opt-specs $arg-handlers
//    }
// ~> complete ''
// ▶ first1
// ▶ first2
// ~> complete '-'
// ▶ (edit:complex-candidate -a &display='-a (Show all)')
// ▶ (edit:complex-candidate --all &display='--all (Show all)')
// ▶ (edit:complex-candidate -n &display='-n (Set name)')
// ~> complete -n ''
// ▶ name1
// ▶ name2
// ~> complete -a ''
// ▶ first1
// ▶ first2
// ~> complete arg1 ''
// ▶ second1
// ▶ second2
// ~> complete arg1 arg2 ''
// ▶ second1
// ▶ second2
// ```

func ( *eval.Frame, , ,  interface{}) error {
	,  := parseGetoptArgs()
	if  != nil {
		return 
	}
	,  := parseGetoptOptSpecs()
	if  != nil {
		return 
	}
	, ,  := parseGetoptArgHandlers()
	if  != nil {
		return 
	}

	// TODO(xiaq): Make the Config field configurable
	 := getopt.Getopt{Options: .opts, Config: getopt.GNUGetoptLong}
	, ,  := .Parse()

	 := .OutputChan()
	 := func( *getopt.Option) {
		 := complexItem{Stem: "-" + string(.Short)}
		if ,  := .desc[];  {
			if ,  := .argDesc[];  {
				.Display = .Stem + " " +  + " (" +  + ")"
			} else {
				.Display = .Stem + " (" +  + ")"
			}
		}
		 <- 
	}
	 := func( *getopt.Option) {
		 := complexItem{Stem: "--" + .Long}
		if ,  := .desc[];  {
			if ,  := .argDesc[];  {
				.Display = .Stem + " " +  + " (" +  + ")"
			} else {
				.Display = .Stem + " (" +  + ")"
			}
		}
		 <- 
	}
	 := func( eval.Callable,  ...interface{}) {
		.Call(, , eval.NoOpts)
	}

	switch .Type {
	case getopt.NewOptionOrArgument, getopt.Argument:
		// Find argument handler.
		var  eval.Callable
		if len() < len() {
			 = [len()]
		} else if  {
			 = [len()-1]
		}
		if  != nil {
			(, .Text)
		} else {
			// TODO(xiaq): Notify that there is no suitable argument completer.
		}
	case getopt.NewOption:
		for ,  := range .opts {
			if .Short != 0 {
				()
			}
			if .Long != "" {
				()
			}
		}
	case getopt.NewLongOption:
		for ,  := range .opts {
			if .Long != "" {
				()
			}
		}
	case getopt.LongOption:
		for ,  := range .opts {
			if strings.HasPrefix(.Long, .Text) {
				()
			}
		}
	case getopt.ChainShortOption:
		for ,  := range .opts {
			if .Short != 0 {
				// TODO(xiaq): Loses chained options.
				()
			}
		}
	case getopt.OptionArgument:
		 := .argGenerator[.Option.Option]
		if  != nil {
			(, .Option.Argument)
		}
	}
	return nil
}

// TODO(xiaq): Simplify most of the parsing below with reflection.

func ( interface{}) ([]string, error) {
	var  []string
	var  error
	 := vals.Iterate(, func( interface{}) bool {
		,  := .(string)
		if ! {
			 = fmt.Errorf("arg should be string, got %s", vals.Kind())
			return false
		}
		 = append(, )
		return true
	})
	if  != nil {
		 = 
	}
	return , 
}

type parsedOptSpecs struct {
	opts         []*getopt.Option
	desc         map[*getopt.Option]string
	argDesc      map[*getopt.Option]string
	argGenerator map[*getopt.Option]eval.Callable
}

func ( interface{}) (parsedOptSpecs, error) {
	 := parsedOptSpecs{
		nil, map[*getopt.Option]string{},
		map[*getopt.Option]string{}, map[*getopt.Option]eval.Callable{}}

	var  error
	 := vals.Iterate(, func( interface{}) bool {
		,  := .(hashmap.Map)
		if ! {
			 = fmt.Errorf("opt should be map, got %s", vals.Kind())
			return false
		}

		 := &getopt.Option{}

		 := func( string) (string, bool, error) {
			,  := .Index()
			if ! {
				return "", false, nil
			}
			if ,  := .(string);  {
				return , true, nil
			}
			return "", false,
				fmt.Errorf("%s should be string, got %s", , vals.Kind())
		}
		 := func( string) (eval.Callable, bool, error) {
			,  := .Index()
			if ! {
				return nil, false, nil
			}
			if ,  := .(eval.Callable);  {
				return , true, nil
			}
			return nil, false,
				fmt.Errorf("%s should be fn, got %s", , vals.Kind())
		}
		 := func( string) (bool, bool, error) {
			,  := .Index()
			if ! {
				return false, false, nil
			}
			if ,  := .(bool);  {
				return , true, nil
			}
			return false, false,
				fmt.Errorf("%s should be bool, got %s", , vals.Kind())
		}

		if , ,  := ("short");  {
			,  := utf8.DecodeRuneInString()
			if  == utf8.RuneError ||  != len() {
				 = fmt.Errorf(
					"short option should be exactly one rune, got %v",
					parse.Quote())
				return false
			}
			.Short = 
		} else if  != nil {
			 = 
			return false
		}
		if , ,  := ("long");  {
			.Long = 
		} else if  != nil {
			 = 
			return false
		}
		if .Short == 0 && .Long == "" {
			 = errors.New(
				"opt should have at least one of short and long forms")
			return false
		}

		, ,  := ("arg-required")
		if  != nil {
			 = 
			return false
		}
		, ,  := ("arg-optional")
		if  != nil {
			 = 
			return false
		}
		switch {
		case  && :
			 = errors.New(
				"opt cannot have both arg-required and arg-optional")
			return false
		case :
			.HasArg = getopt.RequiredArgument
		case :
			.HasArg = getopt.OptionalArgument
		}

		if , ,  := ("desc");  {
			.desc[] = 
		} else if  != nil {
			 = 
			return false
		}
		if , ,  := ("arg-desc");  {
			.argDesc[] = 
		} else if  != nil {
			 = 
			return false
		}
		if , ,  := ("completer");  {
			.argGenerator[] = 
		} else if  != nil {
			 = 
			return false
		}

		.opts = append(.opts, )
		return true
	})
	if  != nil {
		 = 
	}
	return , 
}

func ( interface{}) ([]eval.Callable, bool, error) {
	var  []eval.Callable
	var  bool
	var  error
	 := vals.Iterate(, func( interface{}) bool {
		,  := .(string)
		if  {
			if  == "..." {
				 = true
				return true
			}
			 = fmt.Errorf(
				"string except for ... not allowed as argument handler, got %s",
				parse.Quote())
			return false
		}
		,  := .(eval.Callable)
		if ! {
			 = fmt.Errorf(
				"argument handler should be fn, got %s", vals.Kind())
		}
		 = append(, )
		return true
	})
	if  != nil {
		 = 
	}
	return , , 
}