#ifndef INCLUDED_OPTIONS_
#define INCLUDED_OPTIONS_

#include <string>
#include <set>
#include <unordered_map>

namespace FBB
{
    class Arg;
}

struct Options
{
    enum Value
    {
        UNKNOWN = 0,
        OFF     = 1 << 0,
        ON      = 1 << 1,
        QUIET   = 1 << 2,   // do not show warnings
        WARN    = 1 << 3,   // show warnings
        STD     = 1 << 4,   // do always $$ = $1, no warns
    };

    struct OptInfo
    {
        Value      value;
        std::string filename;
        size_t      lineNr;
    };

    private:
        enum PathType
        {
            FILENAME,
            PATHNAME
        };

        FBB::Arg &d_arg;
    
        std::string const *d_matched;
    
        // The following three data members get their final values in 
        // setAccessorVariables

                            // Resulting values: ON, OFF
        OptInfo d_constructorChecks{OFF, "", 0};

                            // Resulting values: ON, OFF
        OptInfo d_tagMismatches{ON, "", 0};

                            // Resulting values: OFF, QUIET, WARN, STD
        OptInfo d_defaultActions{UNKNOWN, "", 0};

        bool        d_genDebug          = false;
        bool        d_errorVerbose      = false;
        bool        d_flex              = false;
        bool        d_lines             = true;
        bool        d_lspNeeded         = false;
        bool        d_polymorphic       = false;
        bool        d_printTokens       = false;
        bool        d_strongTags        = true;
        bool        d_prompt            = false;
        bool        d_threadSafe        = false;
    
        size_t      d_requiredTokens = 0;
        size_t      d_stackExpansion = 0;

        std::set<std::string> d_warnOptions;// contains the names of options 
                                            // for which Generator may warn
                                            // if specified for already
                                            // existing .h or .ih files

        std::string d_baseClassHeader;
        std::string d_baseClassSkeleton;
        std::string d_polymorphicCodeSkeleton;
        std::string d_polymorphicInline;
        std::string d_polymorphicSkeleton;
        std::string d_classHeader;
        std::string d_className;
        std::string d_classSkeleton;
        std::string d_genericFilename;
        std::string d_implementationHeader;
        std::string d_implementationSkeleton;
        std::string d_locationDecl;
        std::string d_nameSpace;
        std::string d_parsefunSkeleton;
        std::string d_parsefunSource;
        std::string d_preInclude;
        std::string d_scannerInclude;
        std::string d_scannerMatchedTextFunction;
        std::string d_scannerTokenFunction;
        std::string d_scannerClassName;
        std::string d_skeletonDirectory;
        std::string d_stackDecl;
        std::string d_targetDirectory;
        std::string d_verboseName;
        std::string d_tokenPath;
        std::string d_tokenClass;
        std::string d_tokenNameSpace;

        static size_t s_defaultStackExpansion;
    
        static char s_defaultBaseClassSkeleton[];
        static char s_defaultClassName[];
        static char s_defaultClassSkeleton[];
        static char s_defaultImplementationSkeleton[];
        static char s_defaultParsefunSkeleton[];
        static char s_defaultParsefunSource[];
        static char s_defaultPolymorphicCodeSkeleton[];
        static char s_defaultPolymorphicSkeleton[];
        static char s_defaultScannerClassName[];
        static char s_defaultScannerMatchedTextFunction[];
        static char s_defaultScannerTokenFunction[];
        static char s_defaultSkeletonDirectory[];
        static char s_defaultTargetDirectory[];
        static char s_defaultTokenClass[];

        static char s_YYText[];
        static char s_yylex[];
    
        static std::unordered_map<std::string, Value> s_value;

        static Options *s_options;

    public:
        static Options &instance();

        Options(Options const &other) = delete;

        void setMatched(std::string const &matched);                    // f

        void setParsingOptions();

        void setAccessorVariables();

        bool specified(std::string const &option) const;                // f

        void setBaseClassHeader();                                      // f
        void setClassHeader();                                          // f
        void setClassName();                                            // f
        void setGenDebug();                                             // f
        void setErrorVerbose();                                         // f
        void setFlex();                                                 // f
        void setGenericFilename();                                      // f
        void setImplementationHeader();                                 // f
        void setLocationDecl(std::string const &block);
        void setLspNeeded();                                            // f
        void setLtype();
        void setNamespace();                                            // f
        void setParsefunSource();                                       // f
        void setPolymorphicDecl();
        void setPreInclude();                                           // f
        void setPrintTokens();
        void setPrompt();                                               // f
        void setThreadSafe();                                           // f
        void setTokenClass();                                           // f
        void setTokenNameSpace();                                       // f
        void setTokenPath();                                            // f
        void setRequiredTokens(size_t nRequiredTokens);
        void setScannerClassName();                                     // f
        void setScannerInclude();                                       // f
        void setScannerMatchedTextFunction();                           // f
        void setScannerTokenFunction();                                 // f
        void setStackExpansion(size_t nr);
        void setStype();
        void setTargetDirectory();                                      // f
        void setUnionDecl(std::string const &block);
        void setVerbosity();            // Prepare Msg for verbose output

        void setTagMismatches(std::string const &request, 
                                std::string const &filename, size_t lineNr);
        void setDefaultAction(std::string const &request, 
                                std::string const &filename, size_t lineNr);
        void setConstructorChecks(std::string const &request,
                                std::string const &filename, size_t lineNr);

        void unsetLines();                                              // f
        void unsetStrongTags();                                         // f

        void showFilenames() const;


        bool printTokens() const;                                       // f
        bool genDebug() const;                                          // f

        bool errorVerbose() const;                                      // f
        bool lines() const;                                             // f
        bool lspNeeded() const;                                         // f
        bool polymorphic() const;                                       // f
        bool strongTags() const;                                        // f
        bool prompt() const;                                            // f
        bool threadSafe() const;                                        // f
        bool useTokenPath() const;                                      // f

        OptInfo const &tagMismatches() const;                           // f
        OptInfo const &constructorChecks() const;                       // f
        OptInfo const &defaultActions() const;                          // f

        size_t requiredTokens() const;                                  // f
        size_t stackExpansion() const;                                  // f

        std::string const &baseClassSkeleton() const;                   // f
        std::string const &baseClassHeader() const;                     // f
        std::string baseclassHeaderName() const;                        // f
        std::string const &classHeader() const;                         // f
        std::string const &className() const;                           // f
        std::string const &classSkeleton() const;                       // f
        std::string const &implementationHeader() const;                // f
        std::string const &implementationSkeleton() const;              // f
        std::string const &ltype() const;                               // f
        std::string const &nameSpace() const;                           // f
        std::string const &parseSkeleton() const;                       // f
        std::string const &parseSource() const;                         // f
        std::string const &preInclude() const;                          // f
        std::string const &polymorphicCodeSkeleton() const;             // f
        std::string const &polymorphicSkeleton() const;                 // f
        std::string const &scannerClassName() const;                    // f
        std::string const &scannerInclude() const;                      // f
        std::string const &scannerMatchedTextFunction() const;          // f
        std::string const &scannerTokenFunction() const;                // f
        std::string const &skeletonDirectory() const;                   // f
        std::string const &stype() const;                               // f

        std::string const &targetDirectory() const;                     // f

        std::string tokenPath() const;                                  // f

        std::string const &tokenClass() const;                          // f
        std::string const &tokenNameSpace() const;                      // f

        static std::string undelimit(std::string const &str);

    private:
        Options();

            // called by setAccessorVariables()
        void setBooleans();
        void setBasicStrings();
        void setOpt(std::string *destVar, char const *opt, 
                    std::string const &defaultSpec);

        void setQuotedStrings();
        void setPathStrings();  // called by setAccessorVariables,
                                // called by parser.cleanup(). 
                                // inspected Option values
                                // may NOT have directory separators.
        void setSkeletons();
        
                             // undelimit and if append append / if missing
        void cleanDir(std::string &dir, bool append); 
        void addIncludeQuotes(std::string &target);

        std::string const &accept(PathType pathType, char const *declTxt);
        void assign(std::string *target, PathType pathType, 
                    char const *declTxt);

        void setPath(std::string *dest, int optChar, 
                      std::string const &defaultFilename, 
                      char const *defaultSuffix,
                      char const *optionName);

        bool isFirstStypeDefinition() const;

        static Value valueOf(std::string const &key, Value byDefault, 
                                unsigned mask = ~0);
};

#include "options.f"

#endif
