#ifndef _INCLUDED_ALTERNATIVES_H_
#define _INCLUDED_ALTERNATIVES_H_

#include <deque>
#include <vector>
#include <string>

#include <unordered_set>

#include "../enums/enums.h"
#include "../command/command.h"
#include "../history/history.h"


// If, when looking for /t*/m*/ps*/ the initial path /t*/m* does not exist
// then there's no reason for inspecting /t*/mp*/s*/ as it won't exist either.
//
// In those cases the non-existing path is pruned (i.e., t*/m* is) an
// subpatterns of the pruned path (e.g., t*/mp*) are not considered (and so:
// not globbed)

class Options;

class Alternatives: private std::deque<std::string>
{
    using SetStr = std::unordered_set<std::string>;

    struct Entry     // = std::pair<size_t, size_t>; // 1st: inode, 2nd: device
    {
        size_t inode;
        size_t device;
    };

    struct Equal
    {
        bool operator()(Entry const &lhsl, Entry const &rhs) const; // .ih
    };

    struct Hash
    {
        size_t operator()(Entry const &entry) const;                // .ih
    };

    using SetEntry = std::unordered_set<Entry, Hash, Equal>;

    Options const &d_options;

    size_t d_nInHistory;  // nr of items in the history

    bool d_fromHome;      // true: search from $HOME
    bool d_allDirs;       // true: search all dirs (also via links)
    TriState d_addRoot;   // true: always also search /, ifEmpty: only if
                            //       search from $HOME fails

    Command d_command;
    History d_history;  // history constructor uses Options

    std::string d_initialDir;
    SetStr d_ignore;
    SetEntry d_accepted;

    Result d_result;

    public:
        Alternatives();             

        std::string const &operator[](size_t index) const;

        void viable();              // find viable alternatives (or exc.)
        void order();               // handles history-position
        void update(size_t idx);    // put the selected item in the history .f

        size_t separateAt() const;

        Result result() const;                                          // .f

            // by selector/select.cc, selector/selector1.cc
        using std::deque<std::string>::size;

    private:
        void add(char const *path);     // may update  d_nInHistory

                                        // add alternatives found with 
                                        // generalized directory searching
            // globPattern              // starting at 'dir'
            // ^ for reference purposes: names used until version 5
        void addAlternatives(size_t *idx, std::string &searchCmd, 
                             std::string dir);
                             
                                        // add path in 'line' to d_ignored
        void addIgnored(std::string const &line);

                                            // update searchCmd when using
                                            // case insensitive matching
        static void checkCase(size_t *idx, std::string &head);

            // dotPattern
        bool embeddedDot(std::string const &dirEntry) const;

            // globFrom
        void findAlternatives();        // get ignored and alternative dirs, 

            // globFrom, 2nd half
        void findDirs();                // find alternative dirs.

                                        // find alternatives when using
            // globHead                 // generalizedDirs searching
        void generalizedAlternatives(std::string const &initial,
                                     std::string &sarchCmd);

            // generalizedGlob
        Result generalizedDirs(std::string const &initial);

        static std::string getCwd();

            // 2nd part of globPattern
        void globAlternatives(size_t idx, std::string const &searchCmd,
                              std::string const &dir);

            // globFilter
        void inspect(char const *entry); // accept unique/non-relative dirs

                                        // called by embeddedDots
        static bool matchIgnore(std::string const &ignore,
                                std::string const &entry);

                                        // prepare 'dir' for globbing
            // set dir = pattern in     // (in addAlternatives)
            // globPattern
        void prepareDir(std::string &dir, size_t *idx, 
                        std::string &searchCmd);

        void setIgnored();              // determine set of paths to ignore
        void startDir();                // determine the start-dir

                                        // find all dirs using plain
        Result traditionalDirs(std::string const &dir);     // glob from 'dir'

            // trailingDotPattern
        static bool trailingDots(std::string const &spec);
};

#include "alternatives.f"

#endif
