//
// anyoption.h -	include file for command line options management
//
//
// Copyright (C) :      2004,2005,2006,2007,2008,2009,2010
//						European Synchrotron Radiation Facility
//                      BP 220, Grenoble 38043
//                      FRANCE
//
// This file is part of Tango.
//
// Tango is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Tango is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Tango.  If not, see <http://www.gnu.org/licenses/>.

#ifndef _ANYOPTION_H
#define _ANYOPTION_H

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <cstring>

#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 5

#define DEFAULT_MAXOPTS 10
#define MAX_LONG_PREFIX_LENGTH 3

#define DEFAULT_MAXUSAGE 3
#define DEFAULT_MAXHELP 10

#define TRUE_FLAG "true"

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 useFiileName(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 nullptr if no value is set
     */
    char *getValue(const char *_option);
    bool getFlag(const char *_option);
    char *getValue(char _optchar);
    bool getFlag(char _optchar);

    /*
     * Print Usage
     */
    void printUsage();
    void printAutoUsage();
    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 */
    const char **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 */
