#ifndef _INCLUDED_RULES_
#define _INCLUDED_RULES_

#include <iostream>

#include <vector>
#include <set>
#include <string>
#include <algorithm>
#include <unordered_map>

#include "../block/block.h"
#include "../terminal/terminal.h"
#include "../nonterminal/nonterminal.h"
#include "../production/production.h"
#include "../symbol/symbol.h"

    // A Rule contains the information about a rule. 
    // Its number, and a vector of Alternatives. an Alternative is a vector of
    // iterators into the symbol table
class Rules
{ 
    bool d_defaultAction;       // last action was default action:
                                // requires line update at the next
                                // rule definition
    public:
            // For each rule, maintain a record of file/line combinations
            // indicating where the rule was first seen. This allows me to
            // generate warnings when a rule is defined as full rule
            // definitions rather than as alternatives. There's nothing
            // inherently wrong with that, but it may also be a typo causing
            // unexpected conflicts
        using FileInfo = std::pair<std::string, size_t>;

        //FBB: 20160412 - see also sethiddenaction.cc for the use
        //                of the 90000 line number flag that was used to flag
        //                a hidden action. Apparently it was nowhere used
        //                so I decided not to use it anymore.

    private:
        using NFileInfoMap = 
                        std::unordered_map<NonTerminal const *, FileInfo>;
    
        Terminal::Vector d_terminal;        // the vector holding information 
                                            // about defined terminal symbols

        NonTerminal::Vector d_nonTerminal;  // the vector holding information  
                                            // about defined nonterminals
    
        NFileInfoMap d_location;            // the map holding information
                                            // about initial rule locations

        NonTerminal *d_currentRule;         // Pointer to the N currently
                                            // processed. Initially 0
    
        std::string d_startRule;            // name of the startrule
    
        Production::Vector d_production;    // the vector holding information
                                            // about all productions
                                            // productions hold Symbol
                                            // elements, they contain
                                            // information about type and
                                            // index of their elements in the 
                                            // (non)terminal vectors

        Production *d_currentProduction;    // currently processed production
                                            // rule (pointer to a Production
                                            // also in d_production)
    
        static size_t s_acceptProductionNr; // index in d_production of the
                                            // accept rule

        static size_t s_nExpectedConflicts; // how many conflicts to expect?
        static Terminal s_errorTerminal;    // predefined 'error' terminal
        static Terminal s_eofTerminal;      // predefined eof terminal
        static Symbol *s_startSymbol;       // derived from the initial N or
                                            // from the N defined as the
                                            // star tsymbol by
                                            // augmentGrammar().

        static size_t s_lastLineNr;         // last received line nr, used by
                                            // setHiddenAction()
    
    public:
        Rules();                                                        //  f

        static void setExpectedConflicts(size_t value);                 //  f
        static Terminal const *eofTerminal();                           //  f
        static Terminal const *errorTerminal();                         //  f
        static size_t acceptProductionNr();                             //  f
        static size_t expectedConflicts();                              //  f

        void clearLocations();                  // clear d_location     //  f

        Terminal *insert(Terminal *terminal, std::string const &literal); // 1
        NonTerminal *insert(NonTerminal *nonTerminal);                    // 2

        void addElement(Symbol *symbol);
                // add the symbol as the next element of the 
                // rule-production that's currently being defined. 

        void updateDefaultActionLineNr(size_t lineNr);
        void addProduction(size_t lineNr);
                // add a new production to the set of productions of the
                // rule currently being defined

        bool newRule(NonTerminal *nonTerminal, std::string const &source,
                                                size_t lineNr);
                // add a new rule. If startrule has not
                // yet been set, define this rule as the startrule.
                // return true if a really new rule was added, rather than
                // expanding a rule defined earlier.

        void assignNonTerminalNumbers();
        void augmentGrammar(Symbol *start);

        Production const &lastProduction() const;                       //  f

        FileInfo const &fileInfo(NonTerminal const *nt) const;          //  f
                                            // return the FileInfo of the
                                            // first definition of rule `nt'

        std::string const &name() const;    // return the name of the   //  f
                                            // currently defined rule

        std::string const &sType() const;   // return the value type    //  f
                                            // associated with the 
                                            // currently defined rule.

        std::string const &sType(size_t idx) const; // return the value type
                                        // associated with element idx of
                                        // the currently defined production

        Symbol const *symbol(size_t idx) const;                         //  f
                                        // return the symbol
                                        // associated with element idx of
                                        // the currently defined production
                                        // Note: symbol idx MUST exist

        size_t nProductions() const;                                    //  f

        size_t nElements() const;                                       //  f

        void determineFirst();

        bool empty() const;             // true: empty production rule  //  f

        bool hasRules() const;                                          //  f

                // associate an action with the currently defined rule
                // production 
        void setAction(Block const &block, bool defaultAction = false);

        void setHiddenAction(Block const &block);

        void setLastTerminalValue(size_t value);                        //  f

        void setLastPrecedence(size_t value);                           //  f

        void updatePrecedences();       // try to assign a precedence to 
                                        // productions that don't yet have a
                                        // precedence associated to them

        void setPrecedence(Terminal const *terminal);
                // Set the explicit precedence of the currently defined
                // production to the precedence of the given terminal.

        void showFirst() const;         // show the First-sets

        void showTerminals() const;     // show symbolic terminals and their
                                        // values

        void showRules() const;         // show the rule/productions

        void showUnusedTerminals() const;
        void showUnusedNonTerminals() const;
        void showUnusedRules() const;

        void setStartRule(std::string const &start);                    //  f
        std::string const &startRule() const;                           //  f
        Production const *startProduction();                            //  f
        static Symbol const *startSymbol() ;                            //  f

        std::vector<NonTerminal const *> const &nonTerminals() const;   //  f
        std::vector<Terminal const *> const &terminals() const;         //  f
        std::vector<Production const *> const &productions() const;     //  f
        void setNonTerminalTypes();
        void termToNonterm(Symbol *term, Symbol *nonTerm);

    private:
        static bool isTerminal(Symbol const *symbol);
        static void updatePrecedence(Production *production);
};

#include "rules.f"
        
#endif

