#ifndef INCLUDED_PARSER_
#define INCLUDED_PARSER_

#include <iostream>

#include <unordered_map>
#include <string>
#include <vector>
#include <sstream>

#include "../extractable/extractable.h"
#include "../unsignedcastable/unsignedcastable.h"

#include "../typedefs/typedefs.h"
#include "../err/err.h"
#include "../globals/globals.h"
#include "../vsd/vsd.h"

class ConfigLines;

struct Parser
{
    struct OptionInfo
    {
        std::string name;
        std::string value;    
        uint16_t    lineNr;
    };

    typedef std::vector<OptionInfo>     OptionsVect;

    private:
        typedef std::unordered_map<std::string, Parser * > Map;
    
                                            // vectors of LineInfoVects
                                            // each element holds the specs
        std::vector<LineInfoVect> d_lines;  // of one series of keywords

        Map d_map;
    
        static Map s_map;

    public:
        class Lines
        {
            friend class Parser;

            unsigned d_size;                    // initial size

            LineInfoVect::const_iterator d_iter;    // first and last elements
            LineInfoVect::const_iterator d_end;     // of a selected vector
            
            public:
                LineInfo const *get();          // get the lines
                unsigned size() const;
                operator bool() const;

            private:
                Lines(LineInfoVect const &lines);   // no lines found  1.f (ih)

                                                    // >= 1 line found 2.f (ih)
                Lines(LineInfoVect::const_iterator const &begin,
                      LineInfoVect::const_iterator const &end);

                Lines(Lines const &other) = default;

                LineInfoVect::const_iterator begin() const;         // .f (ih)
                LineInfoVect::const_iterator end() const;           // .f (ih)
        };
            
        typedef Map::value_type MapValue;
        
        Parser() = default;
        Parser(Map &&tmp);

                                                    // analysis file        1.
        void load(std::istream &in, uint16_t startLineNr,   
                  StringVect &labels, 
                  OptionsVect &options);

        void load(std::string const &fname);        // continuration file   2.

            // any number (>= 1) lines is OK
        static Lines any(StringVect const &keywords);

            // exactly 1 configuration line is required
        static Lines one(StringVect const &keywords);

            // extract exactly one value associated with keywords
        template <Extractable Type>                                 // .f
        static bool one(StringVect const &keywords, Type &dest);


            // exactly 1 line is required.                          // 1.f
        static bool nonNegative(StringVect const &keywords, double &dest);
        static bool positive(StringVect const &keywords, double &dest); // .f

        template <std::integral Type>                               // 2.f
        static bool nonNegative(StringVect const &keywords, Type &dest);

            // exactly 1 line is required
        static bool proportion(StringVect const &keywords, double &dest);

                    // extract 'dest' variables from lines.get()->tail
                    // returns 'good' if ok, else if no lines then
                    // eof() else fail() + error msg
        template <Extractable ...Types>                                 // 1.f
        static std::istringstream extract(Lines &&ines, 
                                          Types &...dest);

                    // same as 1.f, but LineInfo is already available
                    // extracts from line.tail
        template <Extractable ...Types>                                 // 2.f
        static std::istringstream extract(LineInfo const &line, 
                                          Types &...dest);

                    // extract 'size' 'dest' elements from lines.get()->tail
                    // return values as with extract1.f
        template <Extractable Type>                                     // 3.f
        static std::istringstream extract(Lines &&lines, Type *dest, 
                                                        size_t size);

    private:
        static Lines lines(StringVect const &keywords);

        static std::string sectionList(StringVect const &keywords);

        static bool atLeast(double minimum, 
                            StringVect const &keywords, double &dest);


        template <Extractable Type, Extractable ...Types>               // 4.f
        static bool extract(std::istream &in, Type &first, Types &...more);
        static bool extract(std::istream &in);                  // used in 4.f

        static std::vector<LineInfoVect> const &keywordsLines(
                                            StringVect const &keywords);

        static void setLabels(ConfigLines &parser, StringVect &labels);
        static void setOptions(ConfigLines &parser, OptionsVect &options);

                                        // deepest nesting level in the map
        static bool endPoint(Map::value_type const &mapValue);

                                        // add the next parser line to the
                                        // appropriate map-section
        static void addConfigLine(std::vector<Map *> &mapPtrVect, 
                                  ConfigLines &lines, ParamsSrc src);

        static void locateError(std::vector<Map *> &mapPtrVect, 
                                ConfigLines &lines, ParamsSrc src);

                                                // add (empty) LineInfoVects 
                                                // at s_map's deepest nesting 
        static void addLineInfoVectors(Map &map); //                  levels

};

#include "extract1.f"
#include "extract2.f"
#include "extract3.f"
#include "extract4.f"

#include "one.f"
#include "nonnegative1.f"
#include "nonnegative2.f"

#include "positive.f"

#include "linessize.f"
#include "linesopbool.f"

#endif


