#ifndef COMMANDLINE_H
#define COMMANDLINE_H

#include <cassert>
#include <iostream>
#include <string>
#include <map>
#include <sstream>
#include <algorithm>
#include <cctype>
#include <vector>
#include <set>
using namespace std;

#include <typedefs.h>

/*!
\brief  Process the command line arguments to assign 
        values to variables. 

Acknowledgement: Thanks to Nigel Stewart for ideas. 

\par Example
\verbatim
int main(int argc, char** argv)
{
  commandline cmd(argc,argv);
  
  uint numPoints(10);
  cmd.mapvar(numPoints,"numPoints");
  ...

At the command line calling the program to pass the
  variable/s in.
$./main numPoints=50
\endverbatim
*/
class commandline
{
  /** No default constructor. */
  commandline() { assert(false); }

  /** Container for variable name and value. */
  map<string,string> mp;   
public:

  /** Sequential list of arguments. */
  vector<string> args;

  //
  //  Input Reading Functions

  /** Supply the command line arguments to the constructor. */
  explicit commandline(int argc, char** argv);
  /** Construct the command line from a input stream. */
  explicit commandline(istream & is);
  /** Read the stream for command line arguments. */
  explicit commandline(stringc & arg);

  /** Read the string as a command line. Separate parsing rules to actual
 command line which pre-parses text. */ 
  void read(stringc & arg);

  void read(istream & is);
  /** var=filename , reading the file in.
   Use: load command line arguments from an argument 
     pointing to a file at the command line! */
  void readfile(stringc var);

  //  Binding

  /** Read a string into a variable. */
  template< typename T >
  boolc mapvar(T& var, stringc & key);

  /** Read a variable, if it does not exist then write init to var. */
  template< typename T >
  boolc mapvar(T& var, stringc & key, T const & init);

  /** If the token is present in the command line then the member 
      function is called. */
  template< typename T >
  boolc mapcallback
  ( 
    T & x, 
    void ( T::*p )(),
    stringc & token
  );

  //  Miscellaneous 

  /** Input strings having no = sign are defined as tokens. */
  multiset<string> tokens;
  /** Has the input included the token? */
  boolc hastoken(stringc & token) const
    { return (tokens.find(token)!=tokens.end()); }

  /** List of attempted binds.  */
  vector<string> bindvar;

  /** Place after mapvar calls. -h prints the mapvar strings. */
  ostream & enablehelp(ostream & os=cout) const;

};



// ---------------------------------------------------------
// Implementation 

//  Note: I had difficulty with map<string,string>
//    as my compiler just did not produce workable code
//    with iterating over mp within the commandline class.
//    This was weird. The same code however did work in map_var.


/*!
\brief  Helper class for commandline.
  Read in a variable.
*/
template< typename T >
class map_var
{
  /** The template argument T is expected to be a reference. */
  T var;
  /** The map that contains the initializations. */
  map<string,string>& tbl;
  /** Was the bind successful? */
  bool found;
public:

  /** An attempt is made to bind the variable named by key to 
      a value passed in the _tbl as a string. */
  map_var(T var_, map<string,string>& tbl_, stringc & key) 
    : var(var_), tbl(tbl_)
  {
    map<string,string>::iterator pos = tbl.find(key);
    found = (pos!=tbl.end());
    if (found)
    {
      stringstream ss(tbl[key]);
      ss >> var;
    }
  }

  /** Was the bind successful? */
  boolc foundGet() const { return found; }
  /** Get the variable reference. */
  T operator() () { return var; }
};


/*!
\brief Specialization of class for string values. 
*/
template<>
class map_var<string&>
{
  string& var;
  map<string,string>& tbl;
  bool found;
public:

  map_var(string& var_, map<string,string>& tbl_, stringc & key)
    : var(var_), tbl(tbl_)
  {
    map<string,string>::iterator pos = tbl.find(key);
    found = (pos!=tbl.end());
    if (found)
    {
      var = tbl[key];
    }
  }
  
  boolc foundGet() const { return found; }
  string& operator() () { return var; }
};


/*!
\brief Specialization of class for boolean values. 
*/
template<>
class map_var<bool&>
{
  bool& var;
  map<string,string>& tbl;
  bool found;
public:

  /** Read in a boolean value as true, false, 0 or 1. */
  map_var(bool& var_, map<string,string>& tbl_, stringc & key)
    : var(var_), tbl(tbl_)
  {
    map<string,string>::iterator pos = tbl.find(key);
    found = (pos!=tbl.end());
    if (found)
    {
      string s = tbl[key];
      transform (s.begin(), s.end(), s.begin(), ::tolower );
      if ((s=="false") || (s=="0"))
        var = false;
      if ((s=="true") || (s=="1"))
        var = true;
    }
  }
  
  boolc foundGet() const { return found; }
  bool& operator() () { return var; }
};

template< typename T >
boolc commandline::mapvar(T& var, stringc & key)
{
  bindvar.push_back(key);
  map_var<T&> t(var,mp,key); 
  return t.foundGet();
}

template< typename T >
boolc commandline::mapvar
(
  T& var, 
  stringc & key, 
  T const & init
)
{
  bindvar.push_back(key);
  map_var<T&> t(var,mp,key); 
  boolc res = t.foundGet();
  if (res==false)
    var = init;

  return res;
}

template< typename T >
boolc commandline::mapcallback
( 
  T & x, 
  void ( T::*p )(),
  stringc & token
)
{
  bool found = (tokens.find(token)!=tokens.end());
  if (found)
    (x.*p)();

  return found;
}


#endif




