#pragma once
#include <map>
#include <optional>
#include <string>

#include "klammerset.h"
#include "argument_set.h"
#include "katom.h"

class Klammerset_registry
{
public:
    Klammerset_registry();

    void add(Klammerset klammerset);
    // Parse a @@@klammerset span.  Returns the new Klammerset so the
    // Machine can load its files, or nullopt when the symbol is already
    // registered: a klammerset is loaded once, and a repeated declaration
    // (typically reached through :requires) is skipped, not an error.
    std::optional<Klammerset> add(std::vector<Katom>::iterator begin, std::vector<Katom>::iterator end, std::vector<Katom>& katoms);
    void check_symbol(const std::string& symbol, const Locator& loc) const;
    // Symbol X must be declared in X/X.k, so that the symbol is a function of
    // the path and the already-loaded guard can run before a file is read.
    void check_declaring_file(const std::string& symbol, const Locator& loc) const;

    bool has(const std::string& symbol) const;
    Klammerset get(const std::string& symbol, const Locator& loc) const;

    std::string describe(int margin=2, bool long_format=false) const;

    // Record what a set actually opened (see Klammerset::m_loaded_files).
    void set_loaded_files(const std::string& symbol, const strings_t& files);

    // Every file every registered klammerset read.  A klammer whose
    // definitions all come from these was supplied by a klammerset; one with
    // a definition elsewhere was defined by the input -- which is the
    // distinction "kdesc -i" needs, since it loads a klammerset in order to
    // analyse a file without wanting to LIST the klammerset's own klammers.
    strings_t loaded_files() const;

    std::map<std::string, Klammerset> m_klammersets {};
    std::vector<std::string> m_symbols {};
    Parameter_set m_parameters {};
};

// --- The klammerset search path (symbol -> declaration file) ---

// A bare identifier (letters, digits, underscores; starts with a letter)
// names a klammerset symbol; anything else -- a path component, an
// extension, a space -- is a filename used as given.
bool is_klammerset_symbol(const std::string& name);

// The directories searched for a klammerset symbol, most specific first:
//   1. local_dir -- the directory of whatever names the symbol: the input
//      document's directory (ktext -k), the declaring file's directory
//      (:requires), or the cwd when there is no document
//   2. the KLAMMERTEXT_KLAMMERSETS directories (colon-separated; default
//      ~/.klammertext/klammersets), in listed order
//   3. $KLAMMERTEXT_HOME (the distribution's sets, e.g. sks/sks.k)
// A symbol x resolves to the first <dir>/x/x.k found, so a document-local
// set shadows an installed one, which shadows a distributed one.  (This is
// deliberately the OPPOSITE order from the @eval module path, which puts
// the SKS directories first: a stray util.py next to a document shadowing
// an SKS module is an everyday accident, while a directory named sks/
// holding an sks.k next to a manuscript is not.)
std::vector<std::string> klammerset_search_dirs(const std::string& local_dir);

// Resolve a symbol to its declaration file; first hit wins.  Throws a
// Klammerset_error naming the searched directories when nothing matches.
fs::path resolve_klammerset_symbol(
    const std::string& symbol, const std::string& local_dir, const Locator& loc);

// The symbols available on the search path, with provenance; a symbol
// found again in a later directory is marked as shadowed.  For
// kdesc --klammersets.
std::string describe_klammerset_search(const std::string& local_dir, int margin=2);
