context: // Copyright (c) Microsoft. All rights reserved.
  // Licensed under the MIT license. See LICENSE file in the project root for full license information.
  
  using System;
  using System.Collections.Generic;
  using System.IO;
  
  namespace System.CommandLine
  {
      public sealed partial class ArgumentSyntax
      {
          private readonly IEnumerable<string> _arguments;
          private readonly List<ArgumentCommand> _commands = new List<ArgumentCommand>();
          private readonly List<Argument> _options = new List<Argument>();
          private readonly List<Argument> _parameters = new List<Argument>();
  
          private ArgumentParser _parser;
          private ArgumentCommand _definedCommand;
          private ArgumentCommand _activeCommand;
  
          internal ArgumentSyntax(IEnumerable<string> arguments)
          {
              _arguments = arguments;
  
              ApplicationName = GetApplicationName();
              HandleErrors = true;
              HandleHelp = true;
              HandleResponseFiles = true;
              ErrorOnUnexpectedArguments = true;
          }
  
          public static ArgumentSyntax Parse(IEnumerable<string> arguments, Action<ArgumentSyntax> defineAction)
          {
              if (arguments == null)
                  throw new ArgumentNullException("arguments");
  
              if (defineAction == null)
                  throw new ArgumentNullException("defineAction");
  
              var syntax = new ArgumentSyntax(arguments);
              defineAction(syntax);
              syntax.Validate();
              return syntax;
          }
  
          private void Validate()
          {
              // Check whether help is requested
  
              if (HandleHelp && IsHelpRequested())
              {
                  var helpText = GetHelpText();
                  Console.Error.Write(helpText);
                  Environment.Exit(0);
              }
  
              // Check for invalid or missing command
  
              if (_activeCommand == null && _commands.Any())
              {
                  var unreadCommand = Parser.GetUnreadCommand();
                  var message = unreadCommand == null
                      ? Strings.MissingCommand
                      : string.Format(Strings.UnknownCommandFmt, unreadCommand);
                  ReportError(message);
              }
  
              if (ErrorOnUnexpectedArguments)
              {
                  // Check for invalid options and extra parameters
  
                  foreach (var option in Parser.GetUnreadOptionNames())
                  {
                      var message = string.Format(Strings.InvalidOptionFmt, option);
                      ReportError(message);
                  }
  
                  foreach (var parameter in Parser.GetUnreadParameters())
                  {
                      var message = string.Format(Strings.ExtraParameterFmt, parameter);
                      ReportError(message);
                  }
              }
          }
  
          private bool IsHelpRequested()
          {
              return Parser.GetUnreadOptionNames()
                           .Any(a => string.Equals(a, @"-?", StringComparison.Ordinal) ||
                                     string.Equals(a, @"/?", StringComparison.Ordinal) ||
                                     string.Equals(a, @"-h", StringComparison.Ordinal) ||
                                     string.Equals(a, @"--help", StringComparison.Ordinal));
          }
  
          public void ReportError(string message)
          {
              if (HandleErrors)
              {
                  Console.Error.WriteLine(Strings.ErrorWithMessageFmt, message);
                  Environment.Exit(1);
              }
  
              throw new ArgumentSyntaxException(message);
          }
  
          public ArgumentCommand<T> DefineCommand<T>(string name, T value)
          {
              if (string.IsNullOrEmpty(name))
                  throw new ArgumentException(Strings.NameMissing, "name");
  
              if (!IsValidName(name))
              {
                  var message = string.Format(Strings.CommandNameIsNotValidFmt, name);
                  throw new ArgumentException(message, "name");
              }
  
              if (_commands.Any(c => string.Equals(c.Name, name, StringComparison.Ordinal)))
              {
                  var message = string.Format(Strings.CommandAlreadyDefinedFmt, name);
                  throw new InvalidOperationException(message);
              }
  
              if (_options.Concat(_parameters).Any(c => c.Command == null))
                  throw new InvalidOperationException(Strings.CannotDefineCommandsIfArgumentsExist);
  
              var definedCommand = new ArgumentCommand<T>(name, value);
              _commands.Add(definedCommand);
              _definedCommand = definedCommand;
  
              if (_activeCommand != null)
                  return definedCommand;
  
              if (!Parser.TryParseCommand(name))
                  return definedCommand;
  
              _activeCommand = _definedCommand;
              _activeCommand.MarkActive();
  
              return definedCommand;
          }
  
          public Argument<T> DefineOption<T>(string name, T defaultValue, Func<string, T> valueConverter, bool isRequired)
          {
              if (string.IsNullOrEmpty(name))
                  throw new ArgumentException(Strings.NameMissing, "name");
  
              if (DefinedParameters.Any())
                  throw new InvalidOperationException(Strings.OptionsMustBeDefinedBeforeParameters);
  
              var names = ParseOptionNameList(name);
              var option = new Argument<T>(_definedCommand, names, defaultValue, isRequired);
              _options.Add(option);
  
              if (_activeCommand != _definedCommand)
                  return option;
  
              try
              {
                  T value;
                  bool specified;
                  if (Parser.TryParseOption(option.GetDisplayName(), option.Names, valueConverter, isRequired, out value, out specified))
                  { 
                      option.SetValue(value);
                  }
                  else if (specified)
                  {
                      // No value was provided, but the option was specified and a value wasn't required
                      option.MarkSpecified();
                  }
              }
              catch (ArgumentSyntaxException ex)
              {
                  ReportError(ex.Message);
              }
  
              return option;
          }
  
          public ArgumentList<T> DefineOptionList<T>(string name, IReadOnlyList<T> defaultValue, Func<string, T> valueConverter, bool isRequired)
          {
              if (string.IsNullOrEmpty(name))
                  throw new ArgumentException(Strings.NameMissing, "name");
  
              if (DefinedParameters.Any())
                  throw new InvalidOperationException(Strings.OptionsMustBeDefinedBeforeParameters);
  
              var names = ParseOptionNameList(name);
              var optionList = new ArgumentList<T>(_definedCommand, names, defaultValue, isRequired);
              _options.Add(optionList);
  
              if (_activeCommand != _definedCommand)
                  return optionList;
  
              try
              {
                  IReadOnlyList<T> value;
                  bool specified;
                  if (Parser.TryParseOptionList(optionList.GetDisplayName(), optionList.Names, valueConverter, isRequired, out value, out specified))
                  {
                      optionList.SetValue(value);
                  }
                  else if (specified)
                  {
                      // No value was provided, but the option was specified and a value wasn't required
                      optionList.MarkSpecified();
                  }
              }
              catch (ArgumentSyntaxException ex)
              {
                  ReportError(ex.Message);
              }
  
              return optionList;
          }
  
          public Argument<T> DefineParameter<T>(string name, T defaultValue, Func<string, T> valueConverter)
          {
              if (string.IsNullOrEmpty(name))
                  throw new ArgumentException(Strings.NameMissing, "name");
  
              if (!IsValidName(name))
              {
                  var message = string.Format(Strings.ParameterNameIsNotValidFmt, name);
                  throw new ArgumentException(message, "name");
              }
  
              if (DefinedParameters.Any(p => p.IsList))
                  throw new InvalidOperationException(Strings.ParametersCannotBeDefinedAfterLists);
  
              if (DefinedParameters.Any(p => string.Equals(name, p.Name, StringComparison.OrdinalIgnoreCase)))
              {
                  var message = string.Format(Strings.ParameterAlreadyDefinedFmt, name);
                  throw new InvalidOperationException(message);
              }
  
              var parameter = new Argument<T>(_definedCommand, name, defaultValue);
              _parameters.Add(parameter);
  
  
gt:             if (_activeCommand != _definedCommand)