
#include <string>
#include <vector>
#include <sstream>
#include <stdexcept>
#include <iostream>

using namespace std;

#include "utils.h"

using namespace std;

class Message
  {
public:
  // default constructor does nothing in particular
  Message () { };

  // constructor with supplied Message
  explicit Message (const string & msg) 
    : msg_ (msg) { };

  // copy constructor 
  explicit Message (const Message & rhs) 
    : msg_ (rhs.msg_), args_ (rhs.args_) { };

  // operator=  (assign one Message to another)
  const Message & operator= (const Message & rhs)
    {
    if (this != &rhs)
      {
      msg_ = rhs.msg_;
      args_.assign (rhs.args_.begin (), rhs.args_.end ());
      }
    return *this;
    };

  // supply any argument
  template <typename T>
  Message & operator << (const T & s)  
    { 
    args_.push_back (MAKE_STRING (s)); 
    return *this; 
    };  

  // calculate Message with arguments
  string str() const;
  
  // set new Message (eg. in a different language)
  void setMsg (const string & msg)
    {
    msg_ = msg;    
    };

  // set an argument by position
  void setArg (const int i, const string & arg)
    {
    if (i >= 1 && i <= MAX_ARG)
      {
      // ensure vector large enough
      if (static_cast<int> (args_.size ()) < i)
        args_.resize (i);
      args_ [i - 1] = arg;  // set argument
      }
    };

  // set an argument by position number
  void setArg (const char c, const string & arg)
    {
    setArg (getSubscript (c), arg);
    };

  // get reference to argument by position
  //  eg. msg [5] = "newvalue";
  string & operator[] (int i)
    {
    // force argument to be in range - if not, use maximum
    if (i < 1 || i > MAX_ARG)
      i = MAX_ARG;

    // ensure vector large enough
    if (static_cast<int> (args_.size ()) < i)
      args_.resize (i);
    return args_ [--i];  // get reference to argument
    };

  // get reference to argument by position letter
  //  eg. msg ['b'] = "newvalue";
  string & operator[] (const char c)
    {
    return (*this) [getSubscript (c)]; 
    };

  // clear all arguments
  void clear () { args_.clear (); };

  // get an argument by position number
  string getArg (const int i) const
    {
    if (i >= 1 && i <= static_cast<int> (args_.size ()))
      return args_ [i - 1];  // get argument
    else
      return "";
    };

  // get an argument by position letter
  string getArg (const char c) const
    {
    return getArg (getSubscript (c));
    };

  // get an argument by position number
  string operator[] (const int i) const
    {
    return getArg (i);
    };

  // get an argument by position letter
  string operator[] (const char c) const
    {
    return getArg (getSubscript (c));
    };

protected:

  // member variables
  string msg_;            // the Message with %1 etc. in it
  vector <string> args_;  // the arguments (for replacing %1, %2 etc.)
	enum {MAX_ARG = 62};     // 1 to 9, 0, 'a' to 'z', and A to Z
  
  // helper routine

  int getSubscript (const char c) const;

  };    // end of class Message

// call this to throw a runtime error

/*

  eg.

  try
    {
    THROW_ERROR ("Subscript %1 out of allowable range of %2 to %3",
                          (int) sub << (int) min << (int) max);

    }

  catch(exception & e)
    {
    cout << e.what ();
    }

*/

#define THROW_ERROR(msg, args)\
    throw runtime_error ((Message (msg) << args).str ())

// for simple cases: throw runtime_error ("my bad");

