#ifndef _ANYOPTION_H
#define _ANYOPTION_H

#include <iostream>
#include <fstream>
#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 */
