#ifndef INCLUDED_BOBCAT_ARGCONFIG_
#define INCLUDED_BOBCAT_ARGCONFIG_

#include <unordered_map>
#include <string>
#include <memory>

#include <bobcat/arg>

#include <bobcat/configfile>
#include <bobcat/exception>

namespace FBB
{

class ArgConfig__;

class ArgConfig: public Arg, public ConfigFile
{
    ArgConfig__ *d_ptr;

    static std::unique_ptr<ArgConfig> s_argConfig;
    static char const s_alreadyInitialized[];

    public:
        ArgConfig(ArgConfig const &other) = delete;
        ~ArgConfig();

        using Arg::begin;
        using Arg::end;

// no accept, no long options, no file
        static ArgConfig &initialize(char const *optstring,             // 1
            int argc, char **argv,
            Comment cType = KeepComment,
            SearchCasing sType = SearchCaseSensitive,
            Indices iType = IgnoreIndices);

// no accept, no long options, file,
        static ArgConfig &initialize(char const *optstring,             // 2
            int argc, char **argv,
            std::string const &fname,
            Comment cType = KeepComment,
            SearchCasing sType = SearchCaseSensitive,
            Indices iType = IgnoreIndices);

// no accept, long options, no file,
        static ArgConfig &initialize(char const *optstring,             // 3
            LongOption const *begin, LongOption const *const end,
            int argc, char **argv,
            Comment cType = KeepComment,
            SearchCasing sType = SearchCaseSensitive,
            Indices iType = IgnoreIndices);

// no accept, long options, file,
        static ArgConfig &initialize(char const *optstring,             // 4
            LongOption const *begin, LongOption const *const end,
            int argc, char **argv,
            std::string const &fname,
            Comment cType = KeepComment,
            SearchCasing sType = SearchCaseSensitive,
            Indices iType = IgnoreIndices);

// accept, no long options, no file
        static ArgConfig &initialize(int accept, char const *optstring, // 5
            int argc, char **argv,
            Comment cType = KeepComment,
            SearchCasing sType = SearchCaseSensitive,
            Indices iType = IgnoreIndices);

// accept, no long options, file,
        static ArgConfig &initialize(int accept, char const *optstring, // 6
            int argc, char **argv,
            std::string const &fname,
            Comment cType = KeepComment,
            SearchCasing sType = SearchCaseSensitive,
            Indices iType = IgnoreIndices);

// accept, long options no file,
        static ArgConfig &initialize(int accept, char const *optstring, // 7
            LongOption const *begin, LongOption const *const end,
            int argc, char **argv,
            Comment cType = KeepComment,
            SearchCasing sType = SearchCaseSensitive,
            Indices iType = IgnoreIndices);

// accept, long options, file,
        static ArgConfig &initialize(int accept, char const *optstring, // 8
            LongOption const *begin, LongOption const *const end,
            int argc, char **argv,
            std::string const &fname,
            Comment cType = KeepComment,
            SearchCasing sType = SearchCaseSensitive,
            Indices iType = IgnoreIndices);


        static ArgConfig &instance();

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

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

        char const *operator[](size_t idx) const;   // Arg's []         .f
        std::string const &line(size_t idx) const;  // ConfigFile's []  .f

    private:
        ArgConfig(int accept, char const *optstring,                    // 1
            LongOption const *begin, LongOption const *const end,
            int argc, char **argv,
            Comment cType, SearchCasing sType, Indices iType);

        ArgConfig(int accept, char const *optstring,                    // 2
            LongOption const *begin, LongOption const *const end,
            int argc, char **argv,
            std::string const &fname,
            Comment cType, SearchCasing sType, Indices iType);

        RE_iteratorPair findLongOption(int optChar);
        RE_iteratorPair longConfigOpt(std::string const &longOpt);
};

inline size_t ArgConfig::option(size_t idx, std::string *value,
                                                    int optChar) const
{
    return Arg::option(idx, value, optChar);
}
inline size_t ArgConfig::option(size_t *idx, std::string *value,
                                                    int optChar) const
{
    return Arg::option(idx, value, optChar);
}
inline size_t ArgConfig::option(size_t idx, std::string *value,
                                            char const *longOption) const
{
    return Arg::option(idx, value, longOption);
}
inline size_t ArgConfig::option(size_t *idx, std::string *value,
                                            char const *longOpt) const
{
    return Arg::option(idx, value, longOpt);
}
inline char const *ArgConfig::operator[](size_t idx) const
{
    return Arg::operator[](idx);
}
inline std::string const &ArgConfig::line(size_t idx) const
{
    return ConfigFile::operator[](idx);
}

} // FBB

#endif
