/** @file

    $Id$

    $Date$
    $Author$

    @copy © Marc Wäckerlin
    @license LGPL, see file <a href="license.html">COPYING</a>

    $Log$
    Revision 1.3  2005/02/08 12:31:36  marc
    new static methods to simplify access to options

    Revision 1.2  2004/11/25 18:26:04  marc
    Constness corrected

    Revision 1.1  2004/08/28 16:13:42  marc
    mrw-c++-0.92 (mrw)
    - new file: version.cpp
    - new file header for all sources
    - work around warning in mrw::auto<T>
    - possibility to compile without log4cxx
    - work around bugs in demangle.h and libiberty.h
    - corrections in documentation
    - added simple tracing mechanism
    - more warnings
    - small corrections in Auto<>::Free and a new test for it
    - possibility to compile without stack trace

*/
#include <mrw/arg.hpp>
#include <mrw/exception.hpp>

namespace mrw {
  
  const std::string& Param::Value::toString() const throw(std::exception) {
    throw mrw::bad_cast();
  }
  
  int Param::Value::toInt() const throw(std::exception) {
    throw mrw::bad_cast();
  }

  bool Param::Value::toBool() const throw(std::exception) {
    throw mrw::bad_cast();
  }

  void Param::IntValue::operator=(const std::string& s) throw(std::exception) {
    if (!(std::stringstream(s)>>_i)) throw mrw::bad_cast();
  }

  const mrw::SmartPointer<Param::Value>& Param::operator[](unsigned int i) const
    throw(std::exception) {
    if (i<_params.size()) return _params[i];
    throw mrw::out_of_range
      (((std::stringstream&)
        (std::stringstream()<<"check failed: "
         <<i<<'<'<<_params.size())).str());
  }

  mrw::SmartPointer<Param::Value>& Param::setable(unsigned int i)
    throw(std::exception) {
    if (i<_params.size()) return _params[i];
    throw mrw::out_of_range
      (((std::stringstream&)
        (std::stringstream()<<"check failed: "
         <<i<<'<'<<_params.size())).str());
  }

  Args& Args::operator<<(const mrw::Opt& opt) throw(std::exception) {
    // twice the same, but other sort order
    Options::iterator it(_options.insert(_options.end(), opt));
    if (!_shortopts.insert(ShortOpts::value_type(opt._shortname, it)).second)
      throw mrw::invalid_argument(std::string(1, opt._shortname));
    if (!_longopts.insert(LongOpts::value_type(opt._longname, it)).second)
      throw mrw::invalid_argument(opt._longname);
    return *this;
  }

  Args& Args::operator<<(char const*const*const argv) throw(std::exception) {
    if (_argc<0)
      throw mrw::invalid_argument("argc was not set when shifting argv");
    return parse(_argc, argv);
  }

  const Opt& Args::find(char c) const throw(std::exception) {
    ShortOpts::const_iterator it(_shortopts.find(c));
    if (it==_shortopts.end()) throw mrw::out_of_range(std::string(1, c));
    return *it->second;
  }

  const Opt& Args::find(const std::string& s) const throw(std::exception) {
    LongOpts::const_iterator it(_longopts.find(s));
    if (it==_longopts.end()) throw mrw::out_of_range(s);
    return *it->second;
  }

  Args& Args::parse(int argc, char const*const*const argv)
    throw(std::exception) {
    if (argc>0) _filename = argv[0];
    for (int i(1); i<argc; ++i) {
      std::string arg(argv[i]);
      if (arg.find("--")==0 && arg!="--") {     // long arguments
        LongOpts::iterator it(_longopts.find(arg));
        if (it!=_longopts.end() || i+it->second->args().size()>=argc)
          throw mrw::invalid_argument(arg);
        it->second->set();
        for (int j(0), l(it->second->args().size()); j<l; ++j) {
          *(it->second->args().setable(j)) = argv[++i];
        }
      } else if (arg.find("-")==0) {            // short arguments
        // first check all, then set all
        for (int j(1), l(arg.size()); j<l; ++j) {
          ShortOpts::iterator it(_shortopts.find(arg[j]));
          if (it==_shortopts.end() || it->second->args().size()>0 &&
              (j+1!=l || i+it->second->args().size()>=argc))
            throw mrw::invalid_argument(arg);
        }
        for (int j(1), l(arg.size()); j<l; ++j) {
          ShortOpts::iterator it(_shortopts.find(arg[j]));
          it->second->set();
          if (j+1==l && it->second->args().size()>0) {
            for (int k(0); k < it->second->args().size(); ++k) {
              *(it->second->args().setable(k)) = argv[++i];
            }
          }
        }
      } else {
        if (arg!="--") _otherargs.push_back(arg);
        while (++i<argc) _otherargs.push_back(argv[i]);
      }
    }
    if (_help && find(_help)) help();
    return *this;
  }

}