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

#ifndef BBOT_COMMON_OPTIONS_HXX
#define BBOT_COMMON_OPTIONS_HXX

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

#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 bbot
{
  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 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 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 <bbot/types.hxx>

namespace bbot
{
}

#include <bbot/common-options.ixx>

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

#endif // BBOT_COMMON_OPTIONS_HXX
