#ifndef INCLUDED_BOBCAT_ARGCONFIG_
#define INCLUDED_BOBCAT_ARGCONFIG_

#include <unordered_map>
#include <string>

#include <bobcat/arg>

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

namespace FBB
{

class ArgConfig__;

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

    static 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);

        static ArgConfig &initialize(char const *accept,                // 6
            char const *optstring, 
            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, // 7
            int argc, char **argv,
            std::string const &fname,
            Comment cType = KeepComment,
            SearchCasing sType = SearchCaseSensitive,
            Indices iType = IgnoreIndices);

        static ArgConfig &initialize(char const *accept,                 // 8
            char const *optstring, 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, // 9
            LongOption const *begin, LongOption const *const end,
            int argc, char **argv,
            Comment cType = KeepComment,
            SearchCasing sType = SearchCaseSensitive,
            Indices iType = IgnoreIndices);

        static ArgConfig &initialize(char const *accept,                // 10
            char const *optstring, 
            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, // 11
            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 &initialize(char const *accept,                // 12
            char const *optstring, 
            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) const;                                // 1
        size_t option(std::string const &optchars) const;               // 2
        size_t option(std::string *value, int optChar) const;           // 3
        size_t option(std::string *value, char const *longOption) const; // 4

        size_t option(int option);                                      // 5
        size_t option(std::string const &optchars);                     // 6
        size_t option(std::string *value, int optChar);                 // 7
        size_t option(std::string *value, char const *longOption);      // 8

        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(char const *accept, char const *optstring,            // 1
            LongOption const *begin, LongOption const *const end,
            int argc, char **argv,
            Comment cType, SearchCasing sType, Indices iType);

        ArgConfig(char const *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) const;
        RE_iteratorPair longConfigOpt(std::string const &longOpt) const;

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

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

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
