package arg

import (
	
	
	
	
	
	
	
	
	

	scalar 
)

// path represents a sequence of steps to find the output location for an
// argument or subcommand in the final destination struct
type path struct {
	root   int                   // index of the destination struct
	fields []reflect.StructField // sequence of struct fields to traverse
}

// String gets a string representation of the given path
func ( path) () string {
	 := "args"
	for ,  := range .fields {
		 += "." + .Name
	}
	return 
}

// Child gets a new path representing a child of this path.
func ( path) ( reflect.StructField) path {
	// copy the entire slice of fields to avoid possible slice overwrite
	 := make([]reflect.StructField, len(.fields)+1)
	copy(, .fields)
	[len()-1] = 
	return path{
		root:   .root,
		fields: ,
	}
}

// spec represents a command line option
type spec struct {
	dest          path
	field         reflect.StructField // the struct field from which this option was created
	long          string              // the --long form for this option, or empty if none
	short         string              // the -s short form for this option, or empty if none
	cardinality   cardinality         // determines how many tokens will be present (possible values: zero, one, multiple)
	required      bool                // if true, this option must be present on the command line
	positional    bool                // if true, this option will be looked for in the positional flags
	separate      bool                // if true, each slice and map entry will have its own --flag
	help          string              // the help text for this option
	env           string              // the name of the environment variable for this option, or empty for none
	defaultValue  reflect.Value       // default value for this option
	defaultString string              // default value for this option, in string form to be displayed in help text
	placeholder   string              // placeholder string in help
}

// command represents a named subcommand, or the top-level command
type command struct {
	name        string
	aliases     []string
	help        string
	dest        path
	specs       []*spec
	subcommands []*command
	parent      *command
}

// ErrHelp indicates that the builtin -h or --help were provided
var ErrHelp = errors.New("help requested by user")

// ErrVersion indicates that the builtin --version was provided
var ErrVersion = errors.New("version requested by user")

// for monkey patching in example and test code
var mustParseExit = os.Exit
var mustParseOut io.Writer = os.Stdout

// MustParse processes command line arguments and exits upon failure
func ( ...interface{}) *Parser {
	return mustParse(Config{Exit: mustParseExit, Out: mustParseOut}, ...)
}

// mustParse is a helper that facilitates testing
func mustParse( Config,  ...interface{}) *Parser {
	,  := NewParser(, ...)
	if  != nil {
		fmt.Fprintln(.Out, )
		.Exit(2)
		return nil
	}

	.MustParse(flags())
	return 
}

// Parse processes command line arguments and stores them in dest
func ( ...interface{}) error {
	,  := NewParser(Config{}, ...)
	if  != nil {
		return 
	}
	return .Parse(flags())
}

// flags gets all command line arguments other than the first (program name)
func flags() []string {
	if len(os.Args) == 0 { // os.Args could be empty
		return nil
	}
	return os.Args[1:]
}

// Config represents configuration options for an argument parser
type Config struct {
	// Program is the name of the program used in the help text
	Program string

	// IgnoreEnv instructs the library not to read environment variables
	IgnoreEnv bool

	// IgnoreDefault instructs the library not to reset the variables to the
	// default values, including pointers to sub commands
	IgnoreDefault bool

	// StrictSubcommands intructs the library not to allow global commands after
	// subcommand
	StrictSubcommands bool

	// EnvPrefix instructs the library to use a name prefix when reading environment variables.
	EnvPrefix string

	// Exit is called to terminate the process with an error code (defaults to os.Exit)
	Exit func(int)

	// Out is where help text, usage text, and failure messages are printed (defaults to os.Stdout)
	Out io.Writer
}

// Parser represents a set of command line options with destination values
type Parser struct {
	cmd         *command
	roots       []reflect.Value
	config      Config
	version     string
	description string
	epilogue    string

	// the following field changes during processing of command line arguments
	subcommand []string
}

// Versioned is the interface that the destination struct should implement to
// make a version string appear at the top of the help message.
type Versioned interface {
	// Version returns the version string that will be printed on a line by itself
	// at the top of the help message.
	Version() string
}

// Described is the interface that the destination struct should implement to
// make a description string appear at the top of the help message.
type Described interface {
	// Description returns the string that will be printed on a line by itself
	// at the top of the help message.
	Description() string
}

// Epilogued is the interface that the destination struct should implement to
// add an epilogue string at the bottom of the help message.
type Epilogued interface {
	// Epilogue returns the string that will be printed on a line by itself
	// at the end of the help message.
	Epilogue() string
}

// walkFields calls a function for each field of a struct, recursively expanding struct fields.
func walkFields( reflect.Type,  func( reflect.StructField,  reflect.Type) bool) {
	walkFieldsImpl(, , nil)
}

func walkFieldsImpl( reflect.Type,  func( reflect.StructField,  reflect.Type) bool,  []int) {
	for  := 0;  < .NumField(); ++ {
		 := .Field()
		.Index = make([]int, len()+1)
		copy(.Index, append(, ))
		 := (, )
		if  && .Type.Kind() == reflect.Struct {
			var  []int
			if .Anonymous {
				 = append(, )
			}
			(.Type, , )
		}
	}
}

// NewParser constructs a parser from a list of destination structs
func ( Config,  ...interface{}) (*Parser, error) {
	// fill in defaults
	if .Exit == nil {
		.Exit = os.Exit
	}
	if .Out == nil {
		.Out = os.Stdout
	}

	// first pick a name for the command for use in the usage text
	var  string
	switch {
	case .Program != "":
		 = .Program
	case len(os.Args) > 0:
		 = filepath.Base(os.Args[0])
	default:
		 = "program"
	}

	// construct a parser
	 := Parser{
		cmd:    &command{name: },
		config: ,
	}

	// make a list of roots
	for ,  := range  {
		.roots = append(.roots, reflect.ValueOf())
	}

	// process each of the destination values
	for ,  := range  {
		 := reflect.TypeOf()
		if .Kind() != reflect.Ptr {
			panic(fmt.Sprintf("%s is not a pointer (did you forget an ampersand?)", ))
		}

		,  := cmdFromStruct(, path{root: }, , .EnvPrefix)
		if  != nil {
			return nil, 
		}

		// for backwards compatibility, add nonzero field values as defaults
		// this applies only to the top-level command, not to subcommands (this inconsistency
		// is the reason that this method for setting default values was deprecated)
		for ,  := range .specs {
			// get the value
			 := .val(.dest)

			// if the value is the "zero value" (e.g. nil pointer, empty struct) then ignore
			if isZero() {
				continue
			}

			// store as a default
			.defaultValue = 

			// we need a string to display in help text
			// if MarshalText is implemented then use that
			if ,  := .Interface().(encoding.TextMarshaler);  {
				,  := .MarshalText()
				if  != nil {
					return nil, fmt.Errorf("%v: error marshaling default value to string: %v", .dest, )
				}
				.defaultString = string()
			} else {
				.defaultString = fmt.Sprintf("%v", )
			}
		}

		.cmd.specs = append(.cmd.specs, .specs...)
		.cmd.subcommands = append(.cmd.subcommands, .subcommands...)

		if ,  := .(Versioned);  {
			.version = .Version()
		}
		if ,  := .(Described);  {
			.description = .Description()
		}
		if ,  := .(Epilogued);  {
			.epilogue = .Epilogue()
		}
	}

	// Set the parent of the subcommands to be the top-level command
	// to make sure that global options work when there is more than one
	// dest supplied.
	for ,  := range .cmd.subcommands {
		.parent = .cmd
	}

	return &, nil
}

func cmdFromStruct( string,  path,  reflect.Type,  string) (*command, error) {
	// commands can only be created from pointers to structs
	if .Kind() != reflect.Ptr {
		return nil, fmt.Errorf("subcommands must be pointers to structs but %s is a %s",
			, .Kind())
	}

	 = .Elem()
	if .Kind() != reflect.Struct {
		return nil, fmt.Errorf("subcommands must be pointers to structs but %s is a pointer to %s",
			, .Kind())
	}

	 := command{
		name: ,
		dest: ,
	}

	var  []string
	walkFields(, func( reflect.StructField,  reflect.Type) bool {
		// check for the ignore switch in the tag
		 := .Tag.Get("arg")
		if  == "-" {
			return false
		}

		// if this is an embedded struct then recurse into its fields, even if
		// it is unexported, because exported fields on unexported embedded
		// structs are still writable
		if .Anonymous && .Type.Kind() == reflect.Struct {
			return true
		}

		// ignore any other unexported field
		if !isExported(.Name) {
			return false
		}

		// duplicate the entire path to avoid slice overwrites
		 := .Child()
		 := spec{
			dest:  ,
			field: ,
			long:  strings.ToLower(.Name),
		}

		,  := .Tag.Lookup("help")
		if  {
			.help = 
		}

		// process each comma-separated part of the tag
		var  bool
		for ,  := range strings.Split(, ",") {
			if  == "" {
				continue
			}
			 = strings.TrimLeft(, " ")
			var  string
			if  := strings.Index(, ":");  != -1 {
				 = [+1:]
				 = [:]
			}

			switch {
			case strings.HasPrefix(, "---"):
				 = append(, fmt.Sprintf("%s.%s: too many hyphens", .Name(), .Name))
			case strings.HasPrefix(, "--"):
				.long = [2:]
			case strings.HasPrefix(, "-"):
				if len() > 2 {
					 = append(, fmt.Sprintf("%s.%s: short arguments must be one character only",
						.Name(), .Name))
					return false
				}
				.short = [1:]
			case  == "required":
				.required = true
			case  == "positional":
				.positional = true
			case  == "separate":
				.separate = true
			case  == "help": // deprecated
				.help = 
			case  == "env":
				// Use override name if provided
				if  != "" {
					.env =  + 
				} else {
					.env =  + strings.ToUpper(.Name)
				}
			case  == "subcommand":
				// decide on a name for the subcommand
				var  []string
				if  == "" {
					 = []string{strings.ToLower(.Name)}
				} else {
					 = strings.Split(, "|")
				}
				for  := range  {
					[] = strings.TrimSpace([])
				}

				// parse the subcommand recursively
				,  := ([0], , .Type, )
				if  != nil {
					 = append(, .Error())
					return false
				}

				.aliases = [1:]
				.parent = &
				.help = .Tag.Get("help")

				.subcommands = append(.subcommands, )
				 = true
			default:
				 = append(, fmt.Sprintf("unrecognized tag '%s' on field %s", , ))
				return false
			}
		}

		// placeholder is the string used in the help text like this: "--somearg PLACEHOLDER"
		,  := .Tag.Lookup("placeholder")
		if  {
			.placeholder = 
		} else if .long != "" {
			.placeholder = strings.ToUpper(.long)
		} else {
			.placeholder = strings.ToUpper(.field.Name)
		}

		// if this is a subcommand then we've done everything we need to do
		if  {
			return false
		}

		// check whether this field is supported. It's good to do this here rather than
		// wait until ParseValue because it means that a program with invalid argument
		// fields will always fail regardless of whether the arguments it received
		// exercised those fields.
		var  error
		.cardinality,  = cardinalityOf(.Type)
		if  != nil {
			 = append(, fmt.Sprintf("%s.%s: %s fields are not supported",
				.Name(), .Name, .Type.String()))
			return false
		}

		,  := .Tag.Lookup("default")
		if  {
			// we do not support default values for maps and slices
			if .cardinality == multiple {
				 = append(, fmt.Sprintf("%s.%s: default values are not supported for slice or map fields",
					.Name(), .Name))
				return false
			}

			// a required field cannot also have a default value
			if .required {
				 = append(, fmt.Sprintf("%s.%s: 'required' cannot be used when a default value is specified",
					.Name(), .Name))
				return false
			}

			// parse the default value
			.defaultString = 
			if .Type.Kind() == reflect.Ptr {
				// here we have a field of type *T and we create a new T, no need to dereference
				// in order for the value to be settable
				.defaultValue = reflect.New(.Type.Elem())
			} else {
				// here we have a field of type T and we create a new T and then dereference it
				// so that the resulting value is settable
				.defaultValue = reflect.New(.Type).Elem()
			}
			 := scalar.ParseValue(.defaultValue, )
			if  != nil {
				 = append(, fmt.Sprintf("%s.%s: error processing default value: %v", .Name(), .Name, ))
				return false
			}
		}

		// add the spec to the list of specs
		.specs = append(.specs, &)

		// if this was an embedded field then we already returned true up above
		return false
	})

	if len() > 0 {
		return nil, errors.New(strings.Join(, "\n"))
	}

	// check that we don't have both positionals and subcommands
	var  bool
	for ,  := range .specs {
		if .positional {
			 = true
		}
	}
	if  && len(.subcommands) > 0 {
		return nil, fmt.Errorf("%s cannot have both subcommands and positional arguments", )
	}

	return &, nil
}

// Parse processes the given command line option, storing the results in the fields
// of the structs from which NewParser was constructed.
//
// It returns ErrHelp if "--help" is one of the command line args and ErrVersion if
// "--version" is one of the command line args (the latter only applies if the
// destination struct passed to NewParser implements Versioned.)
//
// To respond to --help and --version in the way that MustParse does, see examples
// in the README under "Custom handling of --help and --version".
func ( *Parser) ( []string) error {
	 := .process()
	if  != nil {
		// If -h or --help were specified then make sure help text supercedes other errors
		for ,  := range  {
			if  == "-h" ||  == "--help" {
				return ErrHelp
			}
			if  == "--" {
				break
			}
		}
	}
	return 
}

func ( *Parser) ( []string) {
	 := .Parse()
	switch {
	case  == ErrHelp:
		.WriteHelpForSubcommand(.config.Out, .subcommand...)
		.config.Exit(0)
	case  == ErrVersion:
		fmt.Fprintln(.config.Out, .version)
		.config.Exit(0)
	case  != nil:
		.FailSubcommand(.Error(), .subcommand...)
	}
}

// process environment vars for the given arguments
func ( *Parser) ( []*spec,  map[*spec]bool) error {
	for ,  := range  {
		if .env == "" {
			continue
		}

		,  := os.LookupEnv(.env)
		if ! {
			continue
		}

		if .cardinality == multiple {
			// expect a CSV string in an environment
			// variable in the case of multiple values
			var  []string
			var  error
			if len(strings.TrimSpace()) > 0 {
				,  = csv.NewReader(strings.NewReader()).Read()
				if  != nil {
					return fmt.Errorf(
						"error reading a CSV string from environment variable %s with multiple values: %v",
						.env,
						,
					)
				}
			}
			if  = setSliceOrMap(.val(.dest), , !.separate);  != nil {
				return fmt.Errorf(
					"error processing environment variable %s with multiple values: %v",
					.env,
					,
				)
			}
		} else {
			if  := scalar.ParseValue(.val(.dest), );  != nil {
				return fmt.Errorf("error processing environment variable %s: %v", .env, )
			}
		}
		[] = true
	}

	return nil
}

// process goes through arguments one-by-one, parses them, and assigns the result to
// the underlying struct field
func ( *Parser) ( []string) error {
	// track the options we have seen
	 := make(map[*spec]bool)

	// union of specs for the chain of subcommands encountered so far
	 := .cmd
	.subcommand = nil

	// make a copy of the specs because we will add to this list each time we expand a subcommand
	 := make([]*spec, len(.specs))
	copy(, .specs)

	// deal with environment vars
	if !.config.IgnoreEnv {
		 := .captureEnvVars(, )
		if  != nil {
			return 
		}
	}

	// determine if the current command has a version option spec
	var  bool
	for ,  := range .specs {
		if .long == "version" {
			 = true
			break
		}
	}

	// process each string from the command line
	var  bool
	var  []string

	// must use explicit for loop, not range, because we manipulate i inside the loop
	for  := 0;  < len(); ++ {
		 := []
		if  == "--" && ! {
			 = true
			continue
		}

		if !isFlag() ||  {
			// each subcommand can have either subcommands or positionals, but not both
			if len(.subcommands) == 0 {
				 = append(, )
				continue
			}

			// if we have a subcommand then make sure it is valid for the current context
			 := findSubcommand(.subcommands, )
			if  == nil {
				return fmt.Errorf("invalid subcommand: %s", )
			}

			// instantiate the field to point to a new struct
			 := .val(.dest)
			if .IsNil() {
				.Set(reflect.New(.Type().Elem())) // we already checked that all subcommands are struct pointers
			}

			// add the new options to the set of allowed options
			if .config.StrictSubcommands {
				 = make([]*spec, len(.specs))
				copy(, .specs)
			} else {
				 = append(, .specs...)
			}

			// capture environment vars for these new options
			if !.config.IgnoreEnv {
				 := .captureEnvVars(.specs, )
				if  != nil {
					return 
				}
			}

			 = 
			.subcommand = append(.subcommand, )
			continue
		}

		// check for special --help and --version flags
		switch  {
		case "-h", "--help":
			return ErrHelp
		case "--version":
			if ! && .version != "" {
				return ErrVersion
			}
		}

		// check for an equals sign, as in "--foo=bar"
		var  string
		 := strings.TrimLeft(, "-")
		if  := strings.Index(, "=");  != -1 {
			 = [+1:]
			 = [:]
		}

		// lookup the spec for this option (note that the "specs" slice changes as
		// we expand subcommands so it is better not to use a map)
		 := findOption(, )
		if  == nil ||  == "" {
			return fmt.Errorf("unknown argument %s", )
		}
		[] = true

		// deal with the case of multiple values
		if .cardinality == multiple {
			var  []string
			if  == "" {
				for +1 < len() && isValue([+1], .field.Type, ) && [+1] != "--" {
					 = append(, [+1])
					++
					if .separate {
						break
					}
				}
			} else {
				 = append(, )
			}
			 := setSliceOrMap(.val(.dest), , !.separate)
			if  != nil {
				return fmt.Errorf("error processing %s: %v", , )
			}
			continue
		}

		// if it's a flag and it has no value then set the value to true
		// use boolean because this takes account of TextUnmarshaler
		if .cardinality == zero &&  == "" {
			 = "true"
		}

		// if we have something like "--foo" then the value is the next argument
		if  == "" {
			if +1 == len() {
				return fmt.Errorf("missing value for %s", )
			}
			if !isValue([+1], .field.Type, ) {
				return fmt.Errorf("missing value for %s", )
			}
			 = [+1]
			++
		}

		 := scalar.ParseValue(.val(.dest), )
		if  != nil {
			return fmt.Errorf("error processing %s: %v", , )
		}
	}

	// process positionals
	for ,  := range  {
		if !.positional {
			continue
		}
		if len() == 0 {
			break
		}
		[] = true
		if .cardinality == multiple {
			 := setSliceOrMap(.val(.dest), , true)
			if  != nil {
				return fmt.Errorf("error processing %s: %v", .placeholder, )
			}
			 = nil
		} else {
			 := scalar.ParseValue(.val(.dest), [0])
			if  != nil {
				return fmt.Errorf("error processing %s: %v", .placeholder, )
			}
			 = [1:]
		}
	}
	if len() > 0 {
		return fmt.Errorf("too many positional arguments at '%s'", [0])
	}

	// fill in defaults and check that all the required args were provided
	for ,  := range  {
		if [] {
			continue
		}

		if .required {
			if .short == "" && .long == "" {
				 := fmt.Sprintf("environment variable %s is required", .env)
				return errors.New()
			}

			 := fmt.Sprintf("%s is required", .placeholder)
			if .env != "" {
				 += " (or environment variable " + .env + ")"
			}

			return errors.New()
		}

		if .defaultValue.IsValid() && !.config.IgnoreDefault {
			// One issue here is that if the user now modifies the value then
			// the default value stored in the spec will be corrupted. There
			// is no general way to "deep-copy" values in Go, and we still
			// support the old-style method for specifying defaults as
			// Go values assigned directly to the struct field, so we are stuck.
			.val(.dest).Set(.defaultValue)
		}
	}

	return nil
}

// isFlag returns true if a token is a flag such as "-v" or "--user" but not "-" or "--"
func isFlag( string) bool {
	return strings.HasPrefix(, "-") && strings.TrimLeft(, "-") != ""
}

// isValue returns true if a token should be consumed as a value for a flag of type t. This
// is almost always the inverse of isFlag. The one exception is for negative numbers, in which
// case we check the list of active options and return true if its not present there.
func isValue( string,  reflect.Type,  []*spec) bool {
	switch .Kind() {
	case reflect.Ptr, reflect.Slice:
		return (, .Elem(), )
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float32, reflect.Float64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		 := reflect.New()
		 := scalar.ParseValue(, )
		// if value can be parsed and is not an explicit option declared elsewhere, then use it as a value
		if  == nil && (!strings.HasPrefix(, "-") || findOption(, strings.TrimPrefix(, "-")) == nil) {
			return true
		}
	}

	// default case that is used in all cases other than negative numbers: inverse of isFlag
	return !isFlag()
}

// val returns a reflect.Value corresponding to the current value for the
// given path
func ( *Parser) ( path) reflect.Value {
	 := .roots[.root]
	for ,  := range .fields {
		if .Kind() == reflect.Ptr {
			if .IsNil() {
				return reflect.Value{}
			}
			 = .Elem()
		}

		 = .FieldByIndex(.Index)
	}
	return 
}

// findOption finds an option from its name, or returns null if no spec is found
func findOption( []*spec,  string) *spec {
	for ,  := range  {
		if .positional {
			continue
		}
		if .long ==  || .short ==  {
			return 
		}
	}
	return nil
}

// findSubcommand finds a subcommand using its name, or returns null if no subcommand is found
func findSubcommand( []*command,  string) *command {
	for ,  := range  {
		if .name ==  {
			return 
		}
		for ,  := range .aliases {
			if  ==  {
				return 
			}
		}
	}
	return nil
}