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

#ifndef LIBBUILD2_COMMON_OPTIONS_HXX
#define LIBBUILD2_COMMON_OPTIONS_HXX

// Begin prologue.
//
#include <libbuild2/export.hxx>
//
// 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 build2
{
  namespace build
  {
    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 LIBBUILD2_SYMEXPORT exception: public std::exception
      {
        public:
        virtual void
        print (::std::ostream&) const = 0;
      };

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

      class LIBBUILD2_SYMEXPORT 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 LIBBUILD2_SYMEXPORT 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 LIBBUILD2_SYMEXPORT 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 LIBBUILD2_SYMEXPORT 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 LIBBUILD2_SYMEXPORT eos_reached: public exception
      {
        public:
        virtual void
        print (::std::ostream&) const;

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

      class LIBBUILD2_SYMEXPORT 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 LIBBUILD2_SYMEXPORT 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_;
      };

      // 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 LIBBUILD2_SYMEXPORT 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 LIBBUILD2_SYMEXPORT 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 LIBBUILD2_SYMEXPORT 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 LIBBUILD2_SYMEXPORT 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_;
      };

      template <typename X>
      struct parser;
    }
  }
}

#include <libbuild2/types.hxx>

#include <libbuild2/options-types.hxx>

namespace build2
{
}

#include <libbuild2/common-options.ixx>

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

#endif // LIBBUILD2_COMMON_OPTIONS_HXX
