#ifndef _INCLUDED_ALTERNATIVES_H_
#define _INCLUDED_ALTERNATIVES_H_

#include <deque>
#include <vector>
#include <set>
#include <string>
#include <memory>

#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: public std::deque<std::string>
{
    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

//    static char const *s_merge[];
//    static char const *const *const s_mergeEnd;

    public:
        enum ViableResult
        {
            ONLY_CD,                // no command, or no solutions found
            RECEIVED_ALTERNATIVES,
        };

        Alternatives();               // uses default

        ViableResult viable();
        void order();
        void update(size_t idx);

        size_t separateAt() const;

    private:
        void getCwd(std::unique_ptr<char> *dest);

        std::string determineInitialDirectory();
        ViableResult globFrom(std::string initial);

        void checkCase(std::string &head, size_t *idx) const;

        void add(char const *path);         // also determines d_nPatterns

        struct GlobContext
        {
            Alternatives &alternatives;
            std::set<std::pair<size_t, size_t> > stored;
            std::set<std::string> ignore;
        };
        ViableResult glob(std::string initial, GlobContext &context);
        ViableResult generalizedGlob(std::string initial,
                                                GlobContext &context);

        void globHead(std::string const &initial,
                      std::string searchCmd, GlobContext &context);
        void globPattern(std::string pattern,
                                std::string &searchCmd, size_t *idx,
                                GlobContext &context);

        static void globFilter(char const *entry, GlobContext &context);

        static void addIgnored(std::string const &line,
                                std::set<std::string> &ignoreSet);

        static bool dotPattern(std::string const &dirEntry, 
                               GlobContext const &globContext);

        static bool matchIgnore(std::string const &ignore,
                                std::string const &entry);

        static bool trailingDotPatterns(std::string const &spec);
};

inline void Alternatives::update(size_t index)
{
    d_history.save((*this)[index]);
}

#endif
