#ifndef INCLUDED_BOBCAT_ARG_
#define INCLUDED_BOBCAT_ARG_

/*
        Singleton Class built around getopt() and getopt_long() (3)
*/


#include <string>
#include <vector>
#include <getopt.h>

namespace FBB
{

class ArgTypes__
{
    protected:
        typedef struct option               OptStruct;
        typedef std::vector<std::string>    StringVector;

    public:
        enum Type
        {
            None        = 0,
            Required    = 1,
            Optional    = 2,
            AsCharOption,
        };
};

class LongOption__: public ArgTypes__
{
    friend class Arg__;
    friend class ArgConfig;

    std::string d_name;
    Type        d_type;
    int         d_optionChar;
    
    public:
        explicit LongOption__(char const *name);
        LongOption__(char const *name, Type type);
        LongOption__(char const *name, int optionChar);

        std::string const &longName() const;    // .f
        int optionChar() const;                 // .f
};

class Arg__;
class Arg: public ArgTypes__
{
    friend class ArgConfig;         // accesses the constructors and
                                    // longOption()

    Arg__ *d_ptr;                   // pointer to the implementation

    static Arg *s_arg;              // points to Singleton Arg
    static char const s_alreadyInitialized[];

    public:
        Arg(Arg const &other) = delete;

        typedef FBB::LongOption__ LongOption;
        typedef std::vector<std::pair<int, std::string>> OptVect;

                                                                        // 1
        static Arg &initialize(char const *optstring, int argc, char **argv);

        static Arg &initialize(int accept,                        // 2
                               char const *optstring, int argc, char **argv);

        static Arg &initialize(char const *optstring,                   // 3
                                LongOption const *const begin,
                                LongOption const *const end,
                                int argc, char **argv);

        static Arg &initialize(int accept, char const *optstring, // 4
                                LongOption const *const begin,
                                LongOption const *const end,
                                int argc, char **argv);

        static Arg &instance();        

        std::string const &argv0() const;
        std::string const &basename() const;
        void help() const;

        size_t nArgs() const;
        size_t beyondDashes() const;

        std::vector<std::string> const &args() const;
        std::vector<std::string>::const_iterator begin() const;
        std::vector<std::string>::const_iterator end() const;

            // total number of specified short (and combined long) options
        size_t nOptions() const;
            // total numer of long-only options specified
        size_t nLongOptions() const;

        size_t option(int option) const;                        // 1
        size_t option(std::string const &optchars) const;       // 2
        size_t option(size_t idx, 
                        std::string *value, int option) const;  // 3
        size_t option(size_t *idx, 
                        std::string *value, int option) const;  // 4
        size_t option(size_t idx, std::string *value, 
                        char const *longOption) const;          // 5
        size_t option(size_t *idx, std::string *value, 
                char const *longOption) const;                  // 6

        size_t option(std::string *value, int optChar) const;   // 1.f
                                                                // 2.f
        size_t option(std::string *value, char const *longOption) const;

        char const *operator[](size_t idx) const;

        void versionHelp(void (*usage)(std::string const &progname), 
            char const *version, size_t minArgs, int helpFlag = 'h', 
            int versionFlag = 'v') const;

        char const **argPointers();                             // 1.cc
        char const **argPointers() const;                       // 2.cc

    protected:
        Arg(int accept, char const *optstring,            // 1
            LongOption const *const begin, LongOption const *const end,
            int argc, char **argv);
};

#include "longname.f"
#include "optionchar.f"
#include "option1.f"
#include "option2.f"

} // FBB

#endif
