#ifndef INCLUDED_OPTLONG_
#define INCLUDED_OPTLONG_

#include <getopt.h>

#include "../usingdecl/usingdecl.h"

// OptLong: wrapper around getopt_long, using a PtrVect instead of a
//          char **argv as provided by main(). 

// See the comment at the bottom of this file

// 'optopt' receives erroneous error characters: not used by GetOptLong

class OptLong: public UsingDecl
{
    using Option = option;

    private:
        PtrVect *d_ptrVect;             // not owned by OptLong
        char const *d_opts;             
        Option const *d_longOpts;

        std::string d_argument;          // stores 'optarg'

        static OptLong s_optLong;

    public:
        static OptLong &instance();                     // returns s_optlong
        static OptLong &init(PtrVect &ptrVect,      // from = 0, inline 1
                             char const *opts, 
                             Option const *longOpts);

                                                    // starting at 'from'
        static OptLong &init(PtrVect &ptrVect, unsigned from,    
                             char const *opts, Option const *longOpts);

        void useErrorMsgs() const;      // active getopt_long error msgs

                                        // option argument (empty if none)
        std::string const &argument() const;

        int next();                     // -1 if no (more) longOption matched
        static size_t nextIdx();        // returns optind
        static void setNextIdx(size_t idx);

    private:
        OptLong();                      // initializes the data     // 1.cc
        OptLong(PtrVect &ptrVect, unsigned from,                    // 3.cc
                char const *opts, Option const *longOpts);

                // resets s_optLong, keeps opterr and tmp's optind settings
        OptLong &operator=(OptLong &&tmp);
};

// static      
inline OptLong &OptLong::instance()
{
    return s_optLong;
}

inline OptLong &OptLong::init(PtrVect &ptrVect, char const *opts, 
                              Option const *longOpts)
{
    return init(ptrVect, 0, opts, longOpts);
}

// static
inline OptLong &OptLong::init(PtrVect &ptrVect, unsigned from,    
                              char const *opts, Option const *longOpts)
{
    return s_optLong = OptLong(ptrVect, from, opts, longOpts);
}

inline void OptLong::useErrorMsgs() const    
{
    opterr = 1;
}

// static  
inline size_t OptLong::nextIdx()
{
    return optind;
}

// static  
inline void OptLong::setNextIdx(size_t idx)
{
    optind = idx;
}

inline std::string const &OptLong::argument() const
{
    return d_argument;
}

    // cf: https://stackoverflow.com/questions/19940100/
    //              is-there-a-way-to-reset-getopt-for-non-global-use
    //
    //    optind = 0;   // resets getopt_long, done by OptBase, in which
    //
    // case argv[optind] is ignored, and remaining argv values specifiy
    // options/arguments processed by getopt_long

#endif
