#ifndef UtlArguments_HH
#define UtlArguments_HH

#include <string>
#include <vector>
#include <sstream>


class UtlArguments {

public:
  
  UtlArguments(int c, const char **v) : argc(c), argv(v) {
  }
  
  
  std::string command() const {
    std::string comm;

    for(int i(0);i<argc;i++) {
      if(i>0) comm+=" ";
      comm+=argv[i];
    }

    return comm;
  }
  
  std::string processName() const {
    return argv[0];
  }
  
  bool option(char c) {
    for(int i(0);i<(argc-1);i++) {
      std::string s(argument(i,""));
      if(s.size()>0 && (s[0]=='-' || s[0]=='+')) {
	for(unsigned j(0);j<s.size();j++) if(s[j]==c) return true;
      }
    }
    return false;
  }
  
  bool option(char c, std::string h) {
    return option(c,false,h);
  }
  
  bool option(char c, bool b, std::string h) {
    if(c=='h') {
      std::cerr << "UtlArguments::option()  -h reserved for help"
		<< std::endl;
    } else {
      if(h!="") {
	if(b) h=std::string(" -")+c+"   (bool)    "+h+", default +"+c+" = true";
	else  h=std::string(" +")+c+"   (bool)    "+h+", default -"+c+" = false";
	_help.push_back(h);
      }
      
      for(int i(0);i<(argc-1);i++) {
	std::string s(argument(i,""));
	if(s.size()>0) {
	  if(s[0]=='+') {
	    for(unsigned j(0);j<s.size();j++) if(s[j]==c) return true;
	  }
	  if(s[0]=='-') {
	    for(unsigned j(0);j<s.size();j++) if(s[j]==c) return false;
	  }
	}
      }
    }
    
    return b;
  }
  
  int optionArgument(char c, int d, std::string h) {
    if(c=='h') {
      std::cerr << "UtlArguments::optionArgument()  -h reserved for help"
		<< std::endl;
    } else {
      if(h!="") {
	std::ostringstream sout;
	sout << d;
	h=std::string(" -")+c+"   (int)     "+h+", default "+sout.str();
	_help.push_back(h);
      }
      
      for(int i(0);i<(argc-2);i++) {
	std::string s(argument(i,""));
	if(s.size()==2 && s[0]=='-' && s[1]==c) {
	  return argument(i+1,d);
	}
      }
    }
    return d;
  }
  
  std::string optionArgument(char c, std::string d, std::string h) {
    if(c=='h') {
      std::cerr << "UtlArguments::optionArgument()  -h reserved for help"
		<< std::endl;
    } else {
      if(h!="") {
	h=std::string(" -")+c+"   (string)  "+h+", default "+d;
	_help.push_back(h);
      }
      
      for(int i(0);i<(argc-2);i++) {
	std::string s(argument(i,""));
	if(s.size()==2 && s[0]=='-' && s[1]==c) {
	  return argument(i+1,d);
	}
      }
    }
    return d;
  }
    
  bool help(std::ostream &o=std::cout) const {
    if(argument(0,"")=="-h") {
      o << std::endl << processName() << " options:" << std::endl;
      for(unsigned i(0);i<_help.size();i++) {
	o << _help[i] << std::endl;
      }
      o << std::endl;
      return true;
    }
    return false;
  }
  
  std::ostream& print(std::ostream &o, std::string s="") {
    o << s << "UtlArguments::print()" << std::endl;
    o << s << " Process name = " << processName() << std::endl;

    o << s << " Number of arguments = " << argc-1 << std::endl;

    for(int i(0);i<(argc-1);i++) {
      o << s << "  Argument " << i << " = " << argument(i,"") << " = " 
	<< argument(i,0) << std::endl;
    }

    bool first(true);
    for(char c('a');c<='z';c++) {
      if(option(c)) {
	if(first) o << " Options =";
	first=false;
	o << " " << c;
      }
    }
    for(char c('A');c<='Z';c++) {
      if(option(c)) {
	if(first) o << " Options =";
	first=false;
	o << " " << c;
      }
    }
    if(!first) o << std::endl;
    return o;
  }

  int lastArgument(int d, std::string h="") {
    if(h!="") {
      std::ostringstream sout;
      sout << d;
      h=std::string(" Last")+" (int)     "+h+", default "+sout.str();
      _help.push_back(h);
    }
    return argument(argc-2,d);
  }

  std::string lastArgument(std::string s, std::string h="") {
    if(h!="") {
      h=std::string(" Last")+" (string)  "+h+", default "+s;
      _help.push_back(h);
    }
    return argument(argc-2,s);
  }

  bool isArgument(int n=0) const {
    return argc>(n+1);
  }
  
  int argument(int n, int d) const {
    if(isArgument(n)) {
      std::istringstream sin(argv[n+1]);
      if(sin.str().size()>2 && sin.str()[0]=='0' && sin.str()[1]=='x') {
	sin >> std::hex >> d;
      } else {
	sin >> d;
      }
    }
    return d;
  }
  
  std::string argument(int n, std::string s) const {
    if(isArgument(n)) s=argv[n+1];
    return s;
  }

  double argument(int n, double d) const {
    if(isArgument(n)) {
      std::istringstream sin(argv[n+1]);
      sin >> d;
    }
    return d;
  }

  unsigned numberOfArguments() const {
    if(argc==0) return 0;
    return argc-1;
  }

  
private:
  const int argc;
  const char **argv;

  std::vector<std::string> _help;
};

#endif
