#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

    public:
        typedef FBB::LongOption__ LongOption;

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

        int beyondDashes() const;

        std::string const &basename() const;
        void help() const;
        size_t nArgs() 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();

    private:
        Arg(Arg const &other) = delete;
        Arg &operator=(Arg const &other) = delete;

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

        Arg(char const *optstring,                              // 2
            LongOption const * const begin,
            LongOption const * const end,
            int argc, char **argv);
};

inline std::string const &LongOption__::longName() const
{
    return d_name;
}
inline int LongOption__::optionChar() const
{
    return d_optionChar;
}
inline size_t Arg::option(std::string *value, int optChar) const
{
    return option(static_cast<size_t>(0), value, optChar);
}
inline size_t Arg::option(std::string *value, char const *longOption) const
{
    return option(static_cast<size_t>(0), value, longOption);
}

} // FBB

#endif
