#ifndef SIMPLECLPARSER_H
#define SIMPLECLPARSER_H


#include "EPropertyList.h"

        /**
           Copyright (C) 2002, stephan@wanderinghorse.net
           Released under the GNU Lesser General Public License

           ESimpleCLParser is an object for handling command line options. Use it like 
           this:

           #include <iostream>
           #include "ESimpleCLParser.h"
           ESimpleCLParser opts;

           #define VERBOSE if( opts.getBool( "verbose", false ) ) cerr

           int main( int argc, char **argv ) {
           opts.setArgs( argc, argv );
           opts.set( "dosomething", ! opts.getBool( "donothing", false ) );

           if( ! opts.getBool( "dosomething", true ) ) { exit( 0 )); }

           int myint = opts.getInt( "width" );
           double myangle = opts.getDouble( "angle", 0.0 );

           VERBOSE << "This is a verbose message." << endl;
           return opts.getInt( "errorcode", 0 );
           }

           (that code's just off the top of my head - it may not compile as-is.)

           See the EPropertyList object's API for a full list of accessor functions.

           Supported command-line formats:
           -foo bar [that is, -foo == "bar"]
           is the same as:
           [--foo bar] [-foo=bar] [--foo=bar]

           -foo -bar false [-foo == true, -bar == false]
           -foo -bar=false [same]
           --double-dashed "some string value"
           --double-dashed="some string value" [same as previous line]

           Whether you use single or double dashes is irrelevant, but you must call
           getXXX() with the same key as was actually passed on the command-line,
           like so:

           int width = opts.getInt( "-width", opts.getInt( "--width", 42 ) );
           will check for -width first, then --width, defaulting to 42.

           Alternately, if you use this form:
           opts.getString( "foo" ); // WITHOUT a dash
           the following options are searched:
           a) foo
           b) -foo
           c) --foo
           so the above call may actually result in getting the value from -foo or
           --foo. This is a potential logic problem if your application uses two
           semantically-different arguments like -force and
           --force. In this case a call to getXXX( "force" ) would find -force
           and not --force. Use getXXX( "-force" ) or getXXX("--force") to
           avoid this ambiguity. The dashed versions of an argument are only
           sought after if getXXX() is called without a dash before the key.


           A note to Qt users: call setArgs() on this object before calling
           QApplication a(argc, argv ), or QApplication will steal any argument
           called -name (and possibly others), removing it from argv. i.e.,
           if you do not call setArgs() on this object first, QApplication may
           steal arguments so you'll never see them. Been there, done that.

           Known Bugs and/or gotchyas:

           Negative numbers:
           --boolflag -123=something
           will be parsed as:
           [--boolflag == true] [-123=something]
           Search the .cpp file for 'negative' and you'll find where this bug lives.
           Since numeric arguments are so rare this is not such a big deal, i think.
           i can think of only a few real-world examples which use args like -1:
           ssh, [GNU] xargs, lpr, ...

           Along those same lines:

           --bool -234 --foobar
           will be parsed as:
           [--bool == -234] [--foobar == true]
           Which i consider to be correct. If you want to set --bool to a negative
           number use: --bool=-123

           i hate the inconsistency this adds, though. :/
        */

        class ESimpleCLParser : public EPropertyList
        {
        public:
                ESimpleCLParser();
                ESimpleCLParser( int argc, char *argv[], int startAt=1 );
                virtual ~ESimpleCLParser();

                /**
                   get/setHelp() text for a given key.
                */
                virtual void setHelp( const std::string &key, const std::string &text );

                virtual const std::string getHelp( const std::string &key ) const;

                /**
                   getString() is overridden to add a special case to all getXXX() calls 
                   made via the EPropertyList API: if a getXXX key ) function is called with
                   a key which does not start with a dash (-) character and they key cannot
                   be found in our list then -key and --key will be tried.

                   This means that, assuming the above sample code is in place, the following 
                   would work:
                   ~/ > myapp --foo=17.34
                   ...
                   double d = opts.getDouble( "foo", 0.0 ); // d == 17.34

                   As will this:
                   opts.set( "--mykey", "myvalue" );
                   ...
                   cerr << "mykey="<< opts.getString( "mykey" ) << endl;


                   Note, however, that command-line arguments passed without a leading dash
                   are not treated as arguments, and will not be inside this object if the command-line
                   arguments are passed in via setArgs().
                   Additionally, it is important to note that if key is passed in with a leading "-"
                   then the additional "dash checking" is NOT done. That is, if you call:
                   opts.getString( "-d", 0.0 );
                   then ONLY the entry -d will match, and not --d.
                */
                virtual std::string getString( const std::string &key, const std::string & defaultVal = std::string() ) const;

                /**
                 * Makes a half-hearted attempt to parse out any args (begining with "-").
                 * Any args without values passed after them are assigned the value true.
                 * Sample valid command lines:
                 *
                 * foo --a --b foo --c bar --f
                 *   (--a and --f == true, --b == "foo" and --c == "bar")
                 * foo --a eat --b this --c "you lazy bum"
                 *   (--a==eat, --b==this, --c=="you lazy bum")
                 * foo --a=b --c d
                 *   (--a == b, --c == d)
                 *
                 * These are identical for purposes of getXXX( "c" ):
                 * [... -c=-1.0 ...] [... -c -1.0 ...] [... --c 1.0 ...] [... --c=1.0 ...]
                 *
                 *
                 * To get the values, call the EPropertyList API functions like:
                 *  int foo = parser.getInt( "--i" ); // getInt("i") now works for -i or --i :)
                 *  bool bar = parser.getBool( "--b" ); // or getBool( "b") for -b or --b
                 * 'startat' says which element in argv[] to start with. This defaults to 1 because it
                 * is assumed that this will be passed from main(), and we don't need the application's
                 * name (argv[0]) in the args list (if it doesn't start with a dash it wouldn't
                 * be treated as an argument, anyway).
                 * If argpre = 0 then it uses the default argument prefix (defaults to "-"). 
                 * If it is >0 then that is used as a char * prefix
                 * for all arguments.
                 *
                 * Note that the values are added to this object (or overwrite existing entries), and the list
                 * is not cleared by this function.
                 */
                virtual int setArgs( int argc, char *argv[], int startAt=0,const char *argpre = "-" );

                /**
                 * Similar to parse( int... ) except that this one reads a whole line of options, parses that into
                 * an array, then passes it to parse(...). Note that this _may_ (but should not, ideally) behave slightly
                 * differently from arguments passed to the other form, which typically come in as command-line args
                 * (parsed by your shell). This functions uses a EStringTokenizer to do it's parsing, so
                 * any differences in behaviour should be resolved there. i am not aware of any differences.
                 */
                virtual int setArgs( std::string args, std::string separators = " " );

                /**
                   Creates a "usage"-like string for this object containing all keys for which
                   setHelp() has been called. If showcurrentvals is true then the current values
                   are also added to th string, otherwise they are left out.

                   Note that the order of the dumped help text/keys is currently undefined,
                   but appears to be alphabetic (this is certainly C++-lib-dependent, though.)
       
                   TODO: maintain the order of args, using the order from setHelp().

                   Sample:
                   in main() do:
                   ESimpleCLParser args;
                   args.setArgs( argc, argv );
                   if( args.getBool( "help", args.getBool( "?", false ) ) )
                   { // triggers on --help, -help, -? or --?
                   cerr << args.dumpHelp();
                   exit( 0 );
                   }

                   Note regarding ELib::libArgs():
                   By default the object returned by ELib::libArgs() might have some help for elib
                   options like e-debug and e-verbose. To remove them simply setHelp( "e-verbose", "" )
                   before calling this function.
                */
                virtual const std::string dumpHelp( bool showcurrentvals = true ) const;

                /**
                   Returns true if any of the following are set to any value: -help, --help, -?, --?
                */
                virtual bool isHelpSet();



        private:
                EPropertyList helpmap;
        };
#endif // SIMPLECLPARSER_H
