#ifndef _ANYOPTION_H
#define _ANYOPTION_H

#include <fstream>
#include <iostream>
#include <stdlib.h>
#include <string>
#include <vector>

#define COMMON_OPT 1
#define COMMAND_OPT 2
#define FILE_OPT 3
#define COMMON_FLAG 4
#define COMMAND_FLAG 5
#define FILE_FLAG 6

#define COMMAND_OPTION_TYPE 1
#define COMMAND_FLAG_TYPE 2
#define FILE_OPTION_TYPE 3
#define FILE_FLAG_TYPE 4
#define UNKNOWN_TYPE_ANY 5

#define DEFAULT_MAXOPTS 10
#define MAX_LONG_PREFIX_LENGTH 4

#define DEFAULT_MAXUSAGE 3
#define DEFAULT_MAXHELP 10

#define TRUE_FLAG "true"

using namespace std;

/** @file anyoption.h @brief Class to parse command line options */
class AnyOption {

      public: /* the public interface */
        AnyOption ();
        AnyOption (int maxoptions);
        AnyOption (int maxoptions, int maxcharoptions);
        ~AnyOption ();

        /*
             * following set methods specifies the
         * special characters and delimiters
         * if not set traditional defaults will be used
             */

        void setCommandPrefixChar (char _prefix);    /* '-' in "-w" */
        void setCommandLongPrefix (char *_prefix);   /* '--' in "--width" */
        void setFileCommentChar (char _comment);     /* '#' in shellscripts */
        void setFileDelimiterChar (char _delimiter); /* ':' in "width : 100" */

        /*
         * provide the input for the options
             * like argv[] for commndline and the
             * option file name  to use;
         */

        void useCommandArgs (int _argc, char **_argv);
        void useFileName (const char *_filename);

        /*
             * turn off the POSIX style options
             * this means anything starting with a '-' or "--"
             * will be considered a valid option
             * which alo means you cannot add a bunch of
             * POIX options chars together like "-lr"  for "-l -r"
             *
             */

        void noPOSIX ();

        /*
             * prints warning verbose if you set anything wrong
             */
        void setVerbose ();

        /*
             * there are two types of options
             *
             * Option - has an associated value ( -w 100 )
             * Flag  - no value, just a boolean flag  ( -nogui )
             *
         * the options can be either a string ( GNU style )
             * or a character ( traditional POSIX style )
             * or both ( --width, -w )
             *
             * the options can be common to the commandline and
             * the optionfile, or can belong only to either of
             * commandline and optionfile
             *
             * following set methods, handle all the aboove
         * cases of options.
             */

        /* options comman to command line and option file */
        void setOption (const char *opt_string);
        void setOption (char opt_char);
        void setOption (const char *opt_string, char opt_char);
        void setFlag (const char *opt_string);
        void setFlag (char opt_char);
        void setFlag (const char *opt_string, char opt_char);

        /* options read from commandline only */
        void setCommandOption (const char *opt_string);
        void setCommandOption (char opt_char);
        void setCommandOption (const char *opt_string, char opt_char);
        void setCommandFlag (const char *opt_string);
        void setCommandFlag (char opt_char);
        void setCommandFlag (const char *opt_string, char opt_char);

        /* options read from an option file only  */
        void setFileOption (const char *opt_string);
        void setFileOption (char opt_char);
        void setFileOption (const char *opt_string, char opt_char);
        void setFileFlag (const char *opt_string);
        void setFileFlag (char opt_char);
        void setFileFlag (const char *opt_string, char opt_char);

        /*
             * process the options, registerd using
             * useCommandArgs() and useFileName();
             */
        void processOptions ();
        void processCommandArgs ();
        void processCommandArgs (int max_args);
        bool processFile ();

        /*
             * process the specified options
             */
        void processCommandArgs (int _argc, char **_argv);
        void processCommandArgs (int _argc, char **_argv, int max_args);
        bool processFile (const char *_filename);

        /*
             * get the value of the options
         * will return NULL if no value is set
             */
        char *getValue (const char *_option);
        bool getFlag (const char *_option);
        char *getValue (char _optchar);
        bool getFlag (char _optchar);

        inline double getDoubleValue (const char *_option, double defaultvalue) {
                double ret;
                if (this->getValue (_option) != 0)
                        ret = atof (this->getValue (_option));
                else
                        ret = defaultvalue;
                return ret;
        };
        inline double getDoubleValue (char _optchar, double defaultvalue) {
                double ret;
                if (this->getValue (_optchar) != 0)
                        ret = atof (this->getValue (_optchar));
                else
                        ret = defaultvalue;
                return ret;
        };

        inline int getIntValue (char _optchar, int defaultvalue) {
                int ret;
                if (this->getValue (_optchar) != 0)
                        ret = atoi (this->getValue (_optchar));
                else
                        ret = defaultvalue;
                return ret;
        };
        inline int getIntValue (const char *_option, int defaultvalue) {
                int ret;
                if (this->getValue (_option) != 0)
                        ret = atoi (this->getValue (_option));
                else
                        ret = defaultvalue;
                return ret;
        };
        inline long getLongValue (const char *_option, long defaultvalue) {
                int ret;
                if (this->getValue (_option) != 0)
                        ret = atol (this->getValue (_option));
                else
                        ret = defaultvalue;
                return ret;
        };

        inline const char *getStringValue (const char *_option, const char *defaultvalue) {
                const char *ret;
                if (this->getValue (_option) != 0)
                        ret = this->getValue (_option);
                else
                        ret = defaultvalue;
                return ret;
        };
        inline const char *getStringValue (char _optchar, const char *defaultvalue) {
                const char *ret;
                if (this->getValue (_optchar) != 0)
                        ret = this->getValue (_optchar);
                else
                        ret = defaultvalue;
                return ret;
        };
        /*
         * Print Usage
         */
        void printUsage ();
        void printAutoUsage ();
        void addUsage (const std::string line);
        void addUsage (const char *line);
        void printHelp ();
        /* print auto usage printing for unknown options or flag */
        void autoUsagePrint (bool flag);

        /*
             * get the argument count and arguments sans the options
             */
        int getArgc ();
        char *getArgv (int index);
        bool hasOptions ();

      private:                /* the hidden data structure */
        int argc;             /* commandline arg count  */
        char **argv;          /* commndline args */
        const char *filename; /* the option file */
        char *appname;        /* the application name from argv[0] */

        int *new_argv;      /* arguments sans options (index to argv) */
        int new_argc;       /* argument count sans the options */
        int max_legal_args; /* ignore extra arguments */

        /* option strings storage + indexing */
        int max_options;      /* maximum number of options */
        const char **options; /* storage */
        int *optiontype;      /* type - common, command, file */
        int *optionindex;     /* index into value storage */
        int option_counter;   /* counter for added options  */

        /* option chars storage + indexing */
        int max_char_options; /* maximum number options */
        char *optionchars;    /*  storage */
        int *optchartype;     /* type - common, command, file */
        int *optcharindex;    /* index into value storage */
        int optchar_counter;  /* counter for added options  */

        /* values */
        char **values;       /* common value storage */
        int g_value_counter; /* globally updated value index LAME! */

        /* help and usage */
        std::vector< std::string > usage; /* usage */
        int max_usage_lines;              /* max usage lines reseverd */
        int usage_lines;                  /* number of usage lines */

        bool command_set;   /* if argc/argv were provided */
        bool file_set;      /* if a filename was provided */
        bool mem_allocated; /* if memory allocated in init() */
        bool posix_style;   /* enables to turn off POSIX style options */
        bool verbose;       /* silent|verbose */
        bool print_usage;   /* usage verbose */
        bool print_help;    /* help verbose */

        char opt_prefix_char;                         /*  '-' in "-w" */
        char long_opt_prefix[MAX_LONG_PREFIX_LENGTH]; /* '--' in "--width" */
        char file_delimiter_char;                     /* ':' in width : 100 */
        char file_comment_char;                       /*  '#' in "#this is a comment" */
        char equalsign;
        char comment;
        char delimiter;
        char endofline;
        char whitespace;
        char nullterminate;

        bool set;  // was static member
        bool once; // was static member

        bool hasoptions;
        bool autousage;

      private: /* the hidden utils */
        void init ();
        void init (int maxopt, int maxcharopt);
        bool alloc ();
        void cleanup ();
        bool valueStoreOK ();

        /* grow storage arrays as required */
        bool doubleOptStorage ();
        bool doubleCharStorage ();
        bool doubleUsageStorage ();

        bool setValue (const char *option, char *value);
        bool setFlagOn (const char *option);
        bool setValue (char optchar, char *value);
        bool setFlagOn (char optchar);

        void addOption (const char *option, int type);
        void addOption (char optchar, int type);
        void addOptionError (const char *opt);
        void addOptionError (char opt);
        bool findFlag (char *value);
        void addUsageError (const char *line);
        bool CommandSet ();
        bool FileSet ();
        bool POSIX ();

        char parsePOSIX (char *arg);
        int parseGNU (char *arg);
        bool matchChar (char c);
        int matchOpt (char *opt);

        /* dot file methods */
        char *readFile ();
        char *readFile (const char *fname);
        bool consumeFile (char *buffer);
        void processLine (char *theline, int length);
        char *chomp (char *str);
        void valuePairs (char *type, char *value);
        void justValue (char *value);

        void printVerbose (const char *msg);
        void printVerbose (char *msg);
        void printVerbose (char ch);
        void printVerbose ();
};

#endif /* ! _ANYOPTION_H */
