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

// Begin prologue.
//
#include <bbot/types-parsers.hxx>
//
// End prologue.

#include <bbot/common-options.hxx>

#include <map>
#include <set>
#include <string>
#include <vector>
#include <utility>
#include <ostream>
#include <sstream>
#include <cstring>

namespace bbot
{
  namespace cli
  {
    // unknown_option
    //
    unknown_option::
    ~unknown_option () noexcept
    {
    }

    void unknown_option::
    print (::std::ostream& os) const
    {
      os << "unknown option '" << option ().c_str () << "'";
    }

    const char* unknown_option::
    what () const noexcept
    {
      return "unknown option";
    }

    // unknown_argument
    //
    unknown_argument::
    ~unknown_argument () noexcept
    {
    }

    void unknown_argument::
    print (::std::ostream& os) const
    {
      os << "unknown argument '" << argument ().c_str () << "'";
    }

    const char* unknown_argument::
    what () const noexcept
    {
      return "unknown argument";
    }

    // missing_value
    //
    missing_value::
    ~missing_value () noexcept
    {
    }

    void missing_value::
    print (::std::ostream& os) const
    {
      os << "missing value for option '" << option ().c_str () << "'";
    }

    const char* missing_value::
    what () const noexcept
    {
      return "missing option value";
    }

    // invalid_value
    //
    invalid_value::
    ~invalid_value () noexcept
    {
    }

    void invalid_value::
    print (::std::ostream& os) const
    {
      os << "invalid value '" << value ().c_str () << "' for option '"
         << option ().c_str () << "'";

      if (!message ().empty ())
        os << ": " << message ().c_str ();
    }

    const char* invalid_value::
    what () const noexcept
    {
      return "invalid option value";
    }

    // eos_reached
    //
    void eos_reached::
    print (::std::ostream& os) const
    {
      os << what ();
    }

    const char* eos_reached::
    what () const noexcept
    {
      return "end of argument stream reached";
    }

    // unexpected_group
    //
    unexpected_group::
    ~unexpected_group () noexcept
    {
    }

    void unexpected_group::
    print (::std::ostream& os) const
    {
      os << "unexpected grouped argument '" << group_ << "' "
         << "for argument '" << argument_ << "'";
    }

    const char* unexpected_group::
    what () const noexcept
    {
      return "unexpected grouped argument";
    }

    // group_separator
    //
    group_separator::
    ~group_separator () noexcept
    {
    }

    void group_separator::
    print (::std::ostream& os) const
    {
      bool ex (!expected_.empty ());
      bool en (!encountered_.empty ());

      if (ex)
      {
        os << "expected group separator '" << expected_ << "'";
        if (en)
          os << " instead of '" << encountered_ << "'";
      }
      else
        os << "unexpected group separator '" << encountered_ << "'";

      if (en)
        os << ", use '\\" << encountered_ << "' to escape";
    }

    const char* group_separator::
    what () const noexcept
    {
      bool ex (!expected_.empty ());
      bool en (!encountered_.empty ());

      return en
        ? ex ? "wrong group separator"    : "unexpected group separator"
        : ex ? "expected group separator" : "";
    }

    // scanner
    //
    scanner::
    ~scanner ()
    {
    }

    // argv_scanner
    //
    bool argv_scanner::
    more ()
    {
      return i_ < argc_;
    }

    const char* argv_scanner::
    peek ()
    {
      if (i_ < argc_)
        return argv_[i_];
      else
        throw eos_reached ();
    }

    const char* argv_scanner::
    next ()
    {
      if (i_ < argc_)
      {
        const char* r (argv_[i_]);

        if (erase_)
        {
          for (int i (i_ + 1); i < argc_; ++i)
            argv_[i - 1] = argv_[i];

          --argc_;
          argv_[argc_] = 0;
        }
        else
          ++i_;

        ++start_position_;
        return r;
      }
      else
        throw eos_reached ();
    }

    void argv_scanner::
    skip ()
    {
      if (i_ < argc_)
      {
        ++i_;
        ++start_position_;
      }
      else
        throw eos_reached ();
    }

    std::size_t argv_scanner::
    position ()
    {
      return start_position_;
    }

    // vector_scanner
    //
    bool vector_scanner::
    more ()
    {
      return i_ < v_.size ();
    }

    const char* vector_scanner::
    peek ()
    {
      if (i_ < v_.size ())
        return v_[i_].c_str ();
      else
        throw eos_reached ();
    }

    const char* vector_scanner::
    next ()
    {
      if (i_ < v_.size ())
        return v_[i_++].c_str ();
      else
        throw eos_reached ();
    }

    void vector_scanner::
    skip ()
    {
      if (i_ < v_.size ())
        ++i_;
      else
        throw eos_reached ();
    }

    std::size_t vector_scanner::
    position ()
    {
      return start_position_ + i_;
    }

    // group_scanner
    //
    bool group_scanner::
    more ()
    {
      // We don't want to call scan_group() here since that
      // would invalidate references to previous arguments.
      // But we do need to check that the previous group was
      // handled.
      //
      if (state_ == scanned)
      {
        if (group_scan_.end () != group_.size ())
          throw unexpected_group (arg_[i_][j_], group_scan_.next ());
      }

      return j_ != 0 || scan_.more ();
    }

    const char* group_scanner::
    peek ()
    {
      if (state_ != peeked)
      {
        scan_group ();
        state_ = peeked;
      }

      // Return unescaped.
      return arg_[i_][j_ - 1].c_str ();
    }

    const char* group_scanner::
    next ()
    {
      if (state_ != peeked)
        scan_group ();
      state_ = scanned;
      // Return unescaped.
      return arg_[i_][--j_].c_str ();
    }

    void group_scanner::
    skip ()
    {
      if (state_ != peeked)
        scan_group ();
      state_ = skipped;
      --j_;
    }

    std::size_t group_scanner::
    position ()
    {
      return j_ == 0 ? scan_.position () : pos_ + (arg_[i_].size () - j_);
    }

    void group_scanner::
    scan_group ()
    {
      // If the previous argument has been scanned, then make
      // sure the group has been scanned (handled) as well.
      //
      if (state_ == scanned)
      {
        if (group_scan_.end () != group_.size ())
          throw unexpected_group (arg_[i_][j_], group_scan_.next ());
      }

      // If we still have arguments in the pack, rewind the group.
      //
      if (j_ != 0)
      {
        group_scan_.reset ();
        return;
      }

      i_ += (i_ == 0 ? 1 : -1);
      group_.clear ();
      group_scan_.reset ();
      pos_ = scan_.position ();

      // Note: using group_ won't cover empty groups and using
      // j_ won't cover single-argument packs.
      //
      bool group (false), pack (false);

      do
      {
        const char* a (scan_.next ());
        size_t i (*a == '\\' ? 1 : 0);
        separator s (sense (a + i));

        if (s == none || i != 0)
        {
          if (arg_[i_].size () != 1)
            arg_[i_].resize (1);

          arg_[i_][0] = a + (s != none ? i : 0);
          j_ = 1;
          break;
        }

        // Start of a leading group for the next argument or
        // argument pack. We will only know which once we see
        // the closing separator.
        //
        if (s != open)
          throw group_separator (a, "");

        size_t n (group_.size ());

        // Scan the group until the closing separator.
        //
        s = none;
        while (s == none && scan_.more ())
        {
          a = scan_.next ();
          i = (*a == '\\' ? 1 : 0);
          s = sense (a + i);

          if (s == none || i != 0)
          {
            group_.push_back (a + (s != none ? i : 0));
            s = none;
          }
        }

        if (s == close)
        {
          size_t m (group_.size ());

          j_ = m - n;
          if (j_ == 0)
            throw group_separator ("{", "");

          if (arg_[i_].size () != j_)
            arg_[i_].resize (j_);

          // Move from group_ to arg_. Add in reverse for ease 
          // of iteration.
          //
          for (size_t j (0); j != j_; ++j)
            arg_[i_][j] = group_[m - j - 1];
          group_.resize (n);

          pack = true;
          break;
        }
        else if (s == close_plus)
          group = true;
        else
          throw group_separator ((s != none ? a : ""), "}+");
      }
      while (scan_.more ());

      // Handle the case where we have seen the leading group
      // but there are no more arguments.
      //
      if (group && j_ == 0)
        throw group_separator ("{", "");

      // Handle trailing groups, if any.
      //
      while (scan_.more ())
      {
        const char* a (scan_.peek ());
        size_t i (*a == '\\' ? 1 : 0);
        separator s (sense (a + i));

        // Next argument, argument pack, or leading group.
        //
        if (s == none || s == open || i != 0)
          break;

        if (s != open_plus)
          throw group_separator (a, "");

        group = true;

        // Scan the group until the closing separator.
        //
        scan_.next ();
        s = none;
        while (s == none && scan_.more ())
        {
          a = scan_.next ();
          i = (*a == '\\' ? 1 : 0);
          s = sense (a + i);

          if (s == none || i != 0)
          {
            group_.push_back (a + (s != none ? i : 0));
            s = none;
          }
        }

        if (s != close)
          throw group_separator ((s != none ? a : ""), "}");
      }

      // Handle the case where we have seen the argument pack
      // without leading or trailing group.
      //
      if (pack && !group)
        throw group_separator ("{", "");
    }

    template <typename X>
    struct parser
    {
      static void
      parse (X& x, bool& xs, scanner& s)
      {
        using namespace std;

        const char* o (s.next ());
        if (s.more ())
        {
          string v (s.next ());
          istringstream is (v);
          if (!(is >> x && is.peek () == istringstream::traits_type::eof ()))
            throw invalid_value (o, v);
        }
        else
          throw missing_value (o);

        xs = true;
      }
    };

    template <>
    struct parser<bool>
    {
      static void
      parse (bool& x, bool& xs, scanner& s)
      {
        const char* o (s.next ());

        if (s.more ())
        {
          const char* v (s.next ());

          if (std::strcmp (v, "1")    == 0 ||
              std::strcmp (v, "true") == 0 ||
              std::strcmp (v, "TRUE") == 0 ||
              std::strcmp (v, "True") == 0)
            x = true;
          else if (std::strcmp (v, "0")     == 0 ||
                   std::strcmp (v, "false") == 0 ||
                   std::strcmp (v, "FALSE") == 0 ||
                   std::strcmp (v, "False") == 0)
            x = false;
          else
            throw invalid_value (o, v);
        }
        else
          throw missing_value (o);

        xs = true;
      }
    };

    template <>
    struct parser<std::string>
    {
      static void
      parse (std::string& x, bool& xs, scanner& s)
      {
        const char* o (s.next ());

        if (s.more ())
          x = s.next ();
        else
          throw missing_value (o);

        xs = true;
      }
    };

    template <typename X>
    struct parser<std::pair<X, std::size_t> >
    {
      static void
      parse (std::pair<X, std::size_t>& x, bool& xs, scanner& s)
      {
        x.second = s.position ();
        parser<X>::parse (x.first, xs, s);
      }
    };

    template <typename X>
    struct parser<std::vector<X> >
    {
      static void
      parse (std::vector<X>& c, bool& xs, scanner& s)
      {
        X x;
        bool dummy;
        parser<X>::parse (x, dummy, s);
        c.push_back (x);
        xs = true;
      }
    };

    template <typename X, typename C>
    struct parser<std::set<X, C> >
    {
      static void
      parse (std::set<X, C>& c, bool& xs, scanner& s)
      {
        X x;
        bool dummy;
        parser<X>::parse (x, dummy, s);
        c.insert (x);
        xs = true;
      }
    };

    template <typename K, typename V, typename C>
    struct parser<std::map<K, V, C> >
    {
      static void
      parse (std::map<K, V, C>& m, bool& xs, scanner& s)
      {
        const char* o (s.next ());

        if (s.more ())
        {
          std::size_t pos (s.position ());
          std::string ov (s.next ());
          std::string::size_type p = ov.find ('=');

          K k = K ();
          V v = V ();
          std::string kstr (ov, 0, p);
          std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));

          int ac (2);
          char* av[] =
          {
            const_cast<char*> (o),
            0
          };

          bool dummy;
          if (!kstr.empty ())
          {
            av[1] = const_cast<char*> (kstr.c_str ());
            argv_scanner s (0, ac, av, false, pos);
            parser<K>::parse (k, dummy, s);
          }

          if (!vstr.empty ())
          {
            av[1] = const_cast<char*> (vstr.c_str ());
            argv_scanner s (0, ac, av, false, pos);
            parser<V>::parse (v, dummy, s);
          }

          m[k] = v;
        }
        else
          throw missing_value (o);

        xs = true;
      }
    };

    template <typename K, typename V, typename C>
    struct parser<std::multimap<K, V, C> >
    {
      static void
      parse (std::multimap<K, V, C>& m, bool& xs, scanner& s)
      {
        const char* o (s.next ());

        if (s.more ())
        {
          std::size_t pos (s.position ());
          std::string ov (s.next ());
          std::string::size_type p = ov.find ('=');

          K k = K ();
          V v = V ();
          std::string kstr (ov, 0, p);
          std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));

          int ac (2);
          char* av[] =
          {
            const_cast<char*> (o),
            0
          };

          bool dummy;
          if (!kstr.empty ())
          {
            av[1] = const_cast<char*> (kstr.c_str ());
            argv_scanner s (0, ac, av, false, pos);
            parser<K>::parse (k, dummy, s);
          }

          if (!vstr.empty ())
          {
            av[1] = const_cast<char*> (vstr.c_str ());
            argv_scanner s (0, ac, av, false, pos);
            parser<V>::parse (v, dummy, s);
          }

          m.insert (typename std::multimap<K, V, C>::value_type (k, v));
        }
        else
          throw missing_value (o);

        xs = true;
      }
    };

    template <typename X, typename T, T X::*M>
    void
    thunk (X& x, scanner& s)
    {
      parser<T>::parse (x.*M, s);
    }

    template <typename X, bool X::*M>
    void
    thunk (X& x, scanner& s)
    {
      s.next ();
      x.*M = true;
    }

    template <typename X, typename T, T X::*M, bool X::*S>
    void
    thunk (X& x, scanner& s)
    {
      parser<T>::parse (x.*M, x.*S, s);
    }
  }
}

#include <map>

namespace bbot
{
}

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

