#ifndef INCLUDED_MODULES_
#define INCLUDED_MODULES_

#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <string>
#include <filesystem>

#include "../support/support.h"
#include "../classes/classes.h"
#include "../moddata/moddata.h"
#include "../scan/scan.h"
#include "../dependencies/dependencies.h"
#include "../compiler/compiler.h"

namespace FBB
{
    class Process;
}

class Options;

class Modules: private Support
{
    using Path          = std::filesystem::path;
    using Set           = std::unordered_set<size_t>;

    using ModMap        = std::unordered_map<std::string, size_t>;
    using ModMapIter    = ModMap::iterator;
    using ModMapInsert  = std::pair<ModMapIter, bool>;

    using DirIter       = std::filesystem::directory_iterator;

    using StrSet        = std::unordered_set<std::string>;
    using ModVect       = std::vector<ModData>;

    using IdxVect       = std::vector<size_t>;

    Options const &d_options;

    Path d_cwd;

    Classes d_classes;      // get the classes dir-names (0 = project)

    IdxVect d_idx;

    ModVect d_modVect;      // modules used in a project
    ModMap d_modNameIdx;    // from name to modVect index

    Scan d_scan;
    Dependencies d_dependencies;

    Set d_unknowns;             // indices of UNKNOWN modules

    public:
        Modules();
        bool fill();            // fill d_scan's modVect: false: no modules
        bool clean();           // remove gcm.cache entries
        bool circular();        // true: circular dependencies          // .f
        void compile();         // compile the module.cc files
        bool objFilenames();    // determine obj filenames (ObjStructs) // .f
        void mark() const;      // mark sources importing modules

    private:
        bool addRequiredBy(Path const &extModgcm);

        void connect(Path const &extModgcm);

        StrSet fillUsers() const;

        bool inExternal(std::string const &dir);

        void inspect(Classes::Info const &info);
        void inspectSubdirs();

        bool linked(Path const &eternalModgcm, size_t unknownIdx);

        void addExtern(Path const &extModgcm);

        bool requiredExternal(Path const &extModGcm);

        void solveUnknown();            // UNKNOWN modules must be at d_extern

        bool unknowns();                // fill d_unknowns, true: found

        bool unkownExternals();         // true: unkown externals remain

        void updateTimes(StrSet const &users) const;
        void writeUsers(StrSet const &users) const;

        static bool needsGcm(std::string &ntbs);
        static bool setLocalType(ModData &data, Path const &source);

        static bool symlink(std::string const &destination, 
                             std::string const &symLink);
};

#include "modules.f"

#endif
