// -*- C++ -*-
//
// This file was generated by CLI, a command line interface
// compiler for C++.
//

#ifndef BPKG_COMMON_OPTIONS_HXX
#define BPKG_COMMON_OPTIONS_HXX

// Begin prologue.
//
//
// End prologue.

#include <list>
#include <deque>
#include <vector>
#include <iosfwd>
#include <string>
#include <cstddef>
#include <exception>

#ifndef CLI_POTENTIALLY_UNUSED
#  if defined(_MSC_VER) || defined(__xlC__)
#    define CLI_POTENTIALLY_UNUSED(x) (void*)&x
#  else
#    define CLI_POTENTIALLY_UNUSED(x) (void)x
#  endif
#endif

namespace bpkg
{
  namespace cli
  {
    class usage_para
    {
      public:
      enum value
      {
        none,
        text,
        option
      };

      usage_para (value);

      operator value () const 
      {
        return v_;
      }

      private:
      value v_;
    };

    class unknown_mode
    {
      public:
      enum value
      {
        skip,
        stop,
        fail
      };

      unknown_mode (value);

      operator value () const 
      {
        return v_;
      }

      private:
      value v_;
    };

    // Exceptions.
    //

    class exception: public std::exception
    {
      public:
      virtual void
      print (::std::ostream&) const = 0;
    };

    ::std::ostream&
    operator<< (::std::ostream&, const exception&);

    class unknown_option: public exception
    {
      public:
      virtual
      ~unknown_option () noexcept;

      unknown_option (const std::string& option);

      const std::string&
      option () const;

      virtual void
      print (::std::ostream&) const;

      virtual const char*
      what () const noexcept;

      private:
      std::string option_;
    };

    class unknown_argument: public exception
    {
      public:
      virtual
      ~unknown_argument () noexcept;

      unknown_argument (const std::string& argument);

      const std::string&
      argument () const;

      virtual void
      print (::std::ostream&) const;

      virtual const char*
      what () const noexcept;

      private:
      std::string argument_;
    };

    class missing_value: public exception
    {
      public:
      virtual
      ~missing_value () noexcept;

      missing_value (const std::string& option);

      const std::string&
      option () const;

      virtual void
      print (::std::ostream&) const;

      virtual const char*
      what () const noexcept;

      private:
      std::string option_;
    };

    class invalid_value: public exception
    {
      public:
      virtual
      ~invalid_value () noexcept;

      invalid_value (const std::string& option,
                     const std::string& value,
                     const std::string& message = std::string ());

      const std::string&
      option () const;

      const std::string&
      value () const;

      const std::string&
      message () const;

      virtual void
      print (::std::ostream&) const;

      virtual const char*
      what () const noexcept;

      private:
      std::string option_;
      std::string value_;
      std::string message_;
    };

    class eos_reached: public exception
    {
      public:
      virtual void
      print (::std::ostream&) const;

      virtual const char*
      what () const noexcept;
    };

    class file_io_failure: public exception
    {
      public:
      virtual
      ~file_io_failure () noexcept;

      file_io_failure (const std::string& file);

      const std::string&
      file () const;

      virtual void
      print (::std::ostream&) const;

      virtual const char*
      what () const noexcept;

      private:
      std::string file_;
    };

    class unmatched_quote: public exception
    {
      public:
      virtual
      ~unmatched_quote () noexcept;

      unmatched_quote (const std::string& argument);

      const std::string&
      argument () const;

      virtual void
      print (::std::ostream&) const;

      virtual const char*
      what () const noexcept;

      private:
      std::string argument_;
    };

    class unexpected_group: public exception
    {
      public:
      virtual
      ~unexpected_group () noexcept;

      unexpected_group (const std::string& argument,
                        const std::string& group);

      const std::string&
      argument () const;

      const std::string&
      group () const;

      virtual void
      print (std::ostream&) const;

      virtual const char*
      what () const noexcept;

      private:
      std::string argument_;
      std::string group_;
    };

    class group_separator: public exception
    {
      public:
      virtual
      ~group_separator () noexcept;

      // Note: either (but not both) can be empty.
      //
      group_separator (const std::string& encountered,
                       const std::string& expected);

      const std::string&
      encountered () const;

      const std::string&
      expected () const;

      virtual void
      print (std::ostream&) const;

      virtual const char*
      what () const noexcept;

      private:
      std::string encountered_;
      std::string expected_;
    };

    // Command line argument scanner interface.
    //
    // The values returned by next() are guaranteed to be valid
    // for the two previous arguments up until a call to a third
    // peek() or next().
    //
    // The position() function returns a monotonically-increasing
    // number which, if stored, can later be used to determine the
    // relative position of the argument returned by the following
    // call to next(). Note that if multiple scanners are used to
    // extract arguments from multiple sources, then the end
    // position of the previous scanner should be used as the
    // start position of the next.
    //
    class scanner
    {
      public:
      virtual
      ~scanner ();

      virtual bool
      more () = 0;

      virtual const char*
      peek () = 0;

      virtual const char*
      next () = 0;

      virtual void
      skip () = 0;

      virtual std::size_t
      position () = 0;
    };

    class argv_scanner: public scanner
    {
      public:
      argv_scanner (int& argc,
                    char** argv,
                    bool erase = false,
                    std::size_t start_position = 0);

      argv_scanner (int start,
                    int& argc,
                    char** argv,
                    bool erase = false,
                    std::size_t start_position = 0);

      int
      end () const;

      virtual bool
      more ();

      virtual const char*
      peek ();

      virtual const char*
      next ();

      virtual void
      skip ();

      virtual std::size_t
      position ();

      protected:
      std::size_t start_position_;
      int i_;
      int& argc_;
      char** argv_;
      bool erase_;
    };

    class vector_scanner: public scanner
    {
      public:
      vector_scanner (const std::vector<std::string>&,
                      std::size_t start = 0,
                      std::size_t start_position = 0);

      std::size_t
      end () const;

      void
      reset (std::size_t start = 0, std::size_t start_position = 0);

      virtual bool
      more ();

      virtual const char*
      peek ();

      virtual const char*
      next ();

      virtual void
      skip ();

      virtual std::size_t
      position ();

      private:
      std::size_t start_position_;
      const std::vector<std::string>& v_;
      std::size_t i_;
    };

    class argv_file_scanner: public argv_scanner
    {
      public:
      argv_file_scanner (int& argc,
                         char** argv,
                         const std::string& option,
                         bool erase = false,
                         std::size_t start_position = 0);

      argv_file_scanner (int start,
                         int& argc,
                         char** argv,
                         const std::string& option,
                         bool erase = false,
                         std::size_t start_position = 0);

      argv_file_scanner (const std::string& file,
                         const std::string& option,
                         std::size_t start_position = 0);

      struct option_info
      {
        // If search_func is not NULL, it is called, with the arg
        // value as the second argument, to locate the options file.
        // If it returns an empty string, then the file is ignored.
        //
        const char* option;
        std::string (*search_func) (const char*, void* arg);
        void* arg;
      };

      argv_file_scanner (int& argc,
                          char** argv,
                          const option_info* options,
                          std::size_t options_count,
                          bool erase = false,
                          std::size_t start_position = 0);

      argv_file_scanner (int start,
                         int& argc,
                         char** argv,
                         const option_info* options,
                         std::size_t options_count,
                         bool erase = false,
                         std::size_t start_position = 0);

      argv_file_scanner (const std::string& file,
                         const option_info* options = 0,
                         std::size_t options_count = 0,
                         std::size_t start_position = 0);

      virtual bool
      more ();

      virtual const char*
      peek ();

      virtual const char*
      next ();

      virtual void
      skip ();

      virtual std::size_t
      position ();

      // Return the file path if the peeked at argument came from a file and
      // the empty string otherwise. The reference is guaranteed to be valid
      // till the end of the scanner lifetime.
      //
      const std::string&
      peek_file ();

      // Return the 1-based line number if the peeked at argument came from
      // a file and zero otherwise.
      //
      std::size_t
      peek_line ();

      private:
      const option_info*
      find (const char*) const;

      void
      load (const std::string& file);

      typedef argv_scanner base;

      const std::string option_;
      option_info option_info_;
      const option_info* options_;
      std::size_t options_count_;

      struct arg
      {
        std::string value;
        const std::string* file;
        std::size_t line;
      };

      std::deque<arg> args_;
      std::list<std::string> files_;

      // Circular buffer of two arguments.
      //
      std::string hold_[2];
      std::size_t i_;

      bool skip_;

      static int zero_argc_;
      static std::string empty_string_;
    };

    class group_scanner: public scanner
    {
      public:
      group_scanner (scanner&);

      virtual bool
      more ();

      virtual const char*
      peek ();

      virtual const char*
      next ();

      virtual void
      skip ();

      virtual std::size_t
      position ();

      // The group is only available after the call to next()
      // (and skip() -- in case one needs to make sure the group
      // was empty, or some such) and is only valid (and must be
      // handled) until the next call to any of the scanner
      // functions (including more()).
      //
      // Note also that argument positions within each group start
      // from 0.
      //
      scanner&
      group ();

      // Escape an argument that is a group separator. Return the
      // passed string if no escaping is required.
      //
      static const char*
      escape (const char*);

      private:
      enum state
      {
        peeked,  // Argument peeked at with peek().
        scanned, // Argument scanned with next().
        skipped, // Argument skipped with skip()/initial.
      };

      enum separator
      {
        none,
        open,      // {
        close,     // }
        open_plus, // +{
        close_plus // }+
      };

      static separator
      sense (const char*);

      // Scan the leading groups, the next argument/argument pack,
      // and the trailing groups.
      //
      void
      scan_group ();

      scanner& scan_;
      state state_;

      // Circular buffer of two arguments.
      //
      std::vector<std::string> arg_[2];
      std::size_t i_, j_, pos_;

      std::vector<std::string> group_;
      vector_scanner group_scan_;
    };

    template <typename X>
    struct parser;
  }
}

#include <set>

#include <bpkg/types.hxx>

#include <bpkg/options-types.hxx>

namespace bpkg
{
  class common_options
  {
    public:
    // Merge options from the specified instance appending/overriding
    // them as if they appeared after options in this instance.
    //
    void
    merge (const common_options&);

    // Option accessors and modifiers.
    //
    const bool&
    v () const;

    bool&
    v ();

    void
    v (const bool&);

    const bool&
    V () const;

    bool&
    V ();

    void
    V (const bool&);

    const bool&
    quiet () const;

    bool&
    quiet ();

    void
    quiet (const bool&);

    const uint16_t&
    verbose () const;

    uint16_t&
    verbose ();

    void
    verbose (const uint16_t&);

    bool
    verbose_specified () const;

    void
    verbose_specified (bool);

    const bpkg::stdout_format&
    stdout_format () const;

    bpkg::stdout_format&
    stdout_format ();

    void
    stdout_format (const bpkg::stdout_format&);

    bool
    stdout_format_specified () const;

    void
    stdout_format_specified (bool);

    const size_t&
    jobs () const;

    size_t&
    jobs ();

    void
    jobs (const size_t&);

    bool
    jobs_specified () const;

    void
    jobs_specified (bool);

    const bool&
    no_result () const;

    bool&
    no_result ();

    void
    no_result (const bool&);

    const string&
    structured_result () const;

    string&
    structured_result ();

    void
    structured_result (const string&);

    bool
    structured_result_specified () const;

    void
    structured_result_specified (bool);

    const bool&
    progress () const;

    bool&
    progress ();

    void
    progress (const bool&);

    const bool&
    no_progress () const;

    bool&
    no_progress ();

    void
    no_progress (const bool&);

    const bool&
    diag_color () const;

    bool&
    diag_color ();

    void
    diag_color (const bool&);

    const bool&
    no_diag_color () const;

    bool&
    no_diag_color ();

    void
    no_diag_color (const bool&);

    const path&
    build () const;

    path&
    build ();

    void
    build (const path&);

    bool
    build_specified () const;

    void
    build_specified (bool);

    const strings&
    build_option () const;

    strings&
    build_option ();

    void
    build_option (const strings&);

    bool
    build_option_specified () const;

    void
    build_option_specified (bool);

    const path&
    fetch () const;

    path&
    fetch ();

    void
    fetch (const path&);

    bool
    fetch_specified () const;

    void
    fetch_specified (bool);

    const strings&
    fetch_option () const;

    strings&
    fetch_option ();

    void
    fetch_option (const strings&);

    bool
    fetch_option_specified () const;

    void
    fetch_option_specified (bool);

    const path&
    curl () const;

    path&
    curl ();

    void
    curl (const path&);

    bool
    curl_specified () const;

    void
    curl_specified (bool);

    const strings&
    curl_option () const;

    strings&
    curl_option ();

    void
    curl_option (const strings&);

    bool
    curl_option_specified () const;

    void
    curl_option_specified (bool);

    const size_t&
    fetch_timeout () const;

    size_t&
    fetch_timeout ();

    void
    fetch_timeout (const size_t&);

    bool
    fetch_timeout_specified () const;

    void
    fetch_timeout_specified (bool);

    const butl::url&
    pkg_proxy () const;

    butl::url&
    pkg_proxy ();

    void
    pkg_proxy (const butl::url&);

    bool
    pkg_proxy_specified () const;

    void
    pkg_proxy_specified (bool);

    const path&
    git () const;

    path&
    git ();

    void
    git (const path&);

    bool
    git_specified () const;

    void
    git_specified (bool);

    const strings&
    git_option () const;

    strings&
    git_option ();

    void
    git_option (const strings&);

    bool
    git_option_specified () const;

    void
    git_option_specified (bool);

    const path&
    sha256 () const;

    path&
    sha256 ();

    void
    sha256 (const path&);

    bool
    sha256_specified () const;

    void
    sha256_specified (bool);

    const strings&
    sha256_option () const;

    strings&
    sha256_option ();

    void
    sha256_option (const strings&);

    bool
    sha256_option_specified () const;

    void
    sha256_option_specified (bool);

    const path&
    tar () const;

    path&
    tar ();

    void
    tar (const path&);

    bool
    tar_specified () const;

    void
    tar_specified (bool);

    const strings&
    tar_option () const;

    strings&
    tar_option ();

    void
    tar_option (const strings&);

    bool
    tar_option_specified () const;

    void
    tar_option_specified (bool);

    const qualified_option<openssl_commands, path>&
    openssl () const;

    qualified_option<openssl_commands, path>&
    openssl ();

    void
    openssl (const qualified_option<openssl_commands, path>&);

    bool
    openssl_specified () const;

    void
    openssl_specified (bool);

    const qualified_option<openssl_commands, strings>&
    openssl_option () const;

    qualified_option<openssl_commands, strings>&
    openssl_option ();

    void
    openssl_option (const qualified_option<openssl_commands, strings>&);

    bool
    openssl_option_specified () const;

    void
    openssl_option_specified (bool);

    const bpkg::auth&
    auth () const;

    bpkg::auth&
    auth ();

    void
    auth (const bpkg::auth&);

    bool
    auth_specified () const;

    void
    auth_specified (bool);

    const std::set<string, icase_compare_string>&
    trust () const;

    std::set<string, icase_compare_string>&
    trust ();

    void
    trust (const std::set<string, icase_compare_string>&);

    bool
    trust_specified () const;

    void
    trust_specified (bool);

    const bool&
    trust_yes () const;

    bool&
    trust_yes ();

    void
    trust_yes (const bool&);

    const bool&
    trust_no () const;

    bool&
    trust_no ();

    void
    trust_no (const bool&);

    const git_capabilities_map&
    git_capabilities () const;

    git_capabilities_map&
    git_capabilities ();

    void
    git_capabilities (const git_capabilities_map&);

    bool
    git_capabilities_specified () const;

    void
    git_capabilities_specified (bool);

    const string&
    pager () const;

    string&
    pager ();

    void
    pager (const string&);

    bool
    pager_specified () const;

    void
    pager_specified (bool);

    const strings&
    pager_option () const;

    strings&
    pager_option ();

    void
    pager_option (const strings&);

    bool
    pager_option_specified () const;

    void
    pager_option_specified (bool);

    const string&
    options_file () const;

    string&
    options_file ();

    void
    options_file (const string&);

    bool
    options_file_specified () const;

    void
    options_file_specified (bool);

    const dir_path&
    default_options () const;

    dir_path&
    default_options ();

    void
    default_options (const dir_path&);

    bool
    default_options_specified () const;

    void
    default_options_specified (bool);

    const bool&
    no_default_options () const;

    bool&
    no_default_options ();

    void
    no_default_options (const bool&);

    const bool&
    keep_tmp () const;

    bool&
    keep_tmp ();

    void
    keep_tmp (const bool&);

    // Print usage information.
    //
    static ::bpkg::cli::usage_para
    print_usage (::std::ostream&,
                 ::bpkg::cli::usage_para = ::bpkg::cli::usage_para::none);

    static ::bpkg::cli::usage_para
    print_long_usage (::std::ostream&,
                      ::bpkg::cli::usage_para = ::bpkg::cli::usage_para::none);

    // Implementation details.
    //
    protected:
    common_options ();

    bool
    _parse (const char*, ::bpkg::cli::scanner&);

    public:
    bool v_;
    bool V_;
    bool quiet_;
    uint16_t verbose_;
    bool verbose_specified_;
    bpkg::stdout_format stdout_format_;
    bool stdout_format_specified_;
    size_t jobs_;
    bool jobs_specified_;
    bool no_result_;
    string structured_result_;
    bool structured_result_specified_;
    bool progress_;
    bool no_progress_;
    bool diag_color_;
    bool no_diag_color_;
    path build_;
    bool build_specified_;
    strings build_option_;
    bool build_option_specified_;
    path fetch_;
    bool fetch_specified_;
    strings fetch_option_;
    bool fetch_option_specified_;
    path curl_;
    bool curl_specified_;
    strings curl_option_;
    bool curl_option_specified_;
    size_t fetch_timeout_;
    bool fetch_timeout_specified_;
    butl::url pkg_proxy_;
    bool pkg_proxy_specified_;
    path git_;
    bool git_specified_;
    strings git_option_;
    bool git_option_specified_;
    path sha256_;
    bool sha256_specified_;
    strings sha256_option_;
    bool sha256_option_specified_;
    path tar_;
    bool tar_specified_;
    strings tar_option_;
    bool tar_option_specified_;
    qualified_option<openssl_commands, path> openssl_;
    bool openssl_specified_;
    qualified_option<openssl_commands, strings> openssl_option_;
    bool openssl_option_specified_;
    bpkg::auth auth_;
    bool auth_specified_;
    std::set<string, icase_compare_string> trust_;
    bool trust_specified_;
    bool trust_yes_;
    bool trust_no_;
    git_capabilities_map git_capabilities_;
    bool git_capabilities_specified_;
    string pager_;
    bool pager_specified_;
    strings pager_option_;
    bool pager_option_specified_;
    string options_file_;
    bool options_file_specified_;
    dir_path default_options_;
    bool default_options_specified_;
    bool no_default_options_;
    bool keep_tmp_;
  };
}

// Print page usage information.
//
namespace bpkg
{
  ::bpkg::cli::usage_para
  print_bpkg_common_options_usage (::std::ostream&,
                                   ::bpkg::cli::usage_para = ::bpkg::cli::usage_para::none);

  ::bpkg::cli::usage_para
  print_bpkg_common_options_long_usage (::std::ostream&,
                                        ::bpkg::cli::usage_para = ::bpkg::cli::usage_para::none);
}

#include <bpkg/common-options.ixx>

// Begin epilogue.
//
//
// End epilogue.

#endif // BPKG_COMMON_OPTIONS_HXX
