/*
This file is part of Msc-generator.
Copyright (C) 2008-2024 Zoltan Turanyi
Distributed under GNU Affero General Public License.

Msc-generator is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Msc-generator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with Msc-generator.  If not, see <http://www.gnu.org/licenses/>.
*/

/** @file cgencommon.h Main file for describing a language and a set of languages.
* @ingroup libcgencommon_files */

#ifndef CGENCOMMON_H
#define CGENCOMMON_H

#include <string>
#include "csh.h"
#include "chartbase.h"

class LanguageCollection;

/** A class to hold all data for a language.*/
class LanguageData
{
    const LanguageCollection* languages;
public:
    const std::string                 description;  ///<Strings presentable to users
    const std::vector<std::string>    extensions;   ///<The list of filename extension triggering this language. No dots, just the extension chars (e.g., "signalling" and not '.signalling')
    const bool                        has_autoheading; ///<Ture, if calculates automatic heading.
    const bool                        has_element_controls; ///<True, if the chart elements support controls.
    const std::string                 entityname; ///<'entity', 'block', 'node' for signalling, block, graph, resp.
    std::string                       default_text; ///<The default text of the chart (in UTF-8)
    std::unique_ptr<Csh>              pCsh;         ///<A fully setup csh parser for the language, containing all data from design libs
    const Chart::ChartFactoryFunction chart_factory;///<The factory function for the language's chart object
    ShapeCollection                   shapes;       ///<The shapes taken from the design library(ies)
    ContextPtrCatalog                 designs;      ///<The designs taken from the design library(ies)
    MscError                          designlib_errors;  ///<The errors during the compilation of the design library(ies)
    bool                              pedantic = false;  ///<Default setting for pedantic (after reading design libs, if any)
    //Contract is that e, c and f are not empty
    LanguageData(std::vector<std::string> e, std::string_view desc, std::string_view ent, std::string_view d,
                 std::unique_ptr<Csh> &&c, bool ah, bool ec, Chart::ChartFactoryFunction f, const LanguageCollection *languages):
        languages(languages),
        description(desc), extensions(e),
        has_autoheading(ah), has_element_controls(ec), entityname(ent),
        default_text(d), pCsh(std::move(c)), chart_factory(f)
        { pCsh->pShapes = &shapes; }
    LanguageData(const LanguageData&) = delete; //we dont want to duplicate pCsh
    LanguageData(LanguageData&&) = default; //easiest not to kill it for now
    ~LanguageData() = default;
    /** True, if the language uses this extension. */
    bool HasExtension(std::string_view e) const noexcept { return std::find(extensions.begin(), extensions.end(), e) != extensions.end(); }
    const std::string& GetName() const noexcept { return extensions.front(); } ///<Get the name of the language (=the primary extension)
    [[nodiscard]] std::unique_ptr<Chart> create_chart(Chart::FileReadProcedure* p, void* param) const { return chart_factory(p, param, languages); }
};


/** A store of Csh descendants, one for each language.
 * Each application using cgencommon is envisioned to have a single instance
 * of this class, where it registers each language at startup.
 * The "chart" object of the language (used when compiling and drawing) is supposed
 * to do a full parse and is envisioned to be cheap to instantiate. Thus a factory
 * function is needed at registration, which creates a fresh instance. There is a second
 * kind of class for each language, the Color Syntax Highlighting class (Csh), which
 * also parses the input text, but does not fully compile, merely collects coloring info
 * and hints (based on where the cursor is) used at auto completion and hinting.
 * Csh objects are deemed expensive to create, since at startup they collect various
 * information, such as what are the valid keywords, attributes, etc. Therefore,
 * at language registration we provide a living copy and re-use this instance for every
 * coloring parse pass.
 * Charts are supposed to override a set of abstract virtual functions of class Chart,
 * which provide more information about this language (description, name, file extensions,
 * etc.).
 * There is also the concept of 'design libraries', which are definitions in a language
 * defined in one file - but usable in all charts of that language. They typically
 * contain chart designs and shapes. Every chart has a function to export the collected
 * designs and shapes so far (along with the errors happened during their parsing). These
 * functions are used to fill in LanguageData::shapes, LanguageData::designs and
 * LanguageData::designlib_errors. Doing this filling is the responsibility of the
 * application. It must locate and parse the design libraries (for each supported
 * language) and copy the designs and shapes to the relevant LanguageData object.
 */
class LanguageCollection
{
protected:
    std::list<LanguageData> languages; ///<Stores our supported languages. Must be a list as LanguageData is neither copyable, nor movable.
    /** Libraries used in the system (with version number), mapping to their license text. */
    std::map<std::string, std::string> libraries;
public:
    LanguageCollection() = default;
    //Since LanguageData references the collection, we make this non-movable, non-copyable
    LanguageCollection(LanguageCollection&&) = delete;
    LanguageCollection(const LanguageCollection&) = delete;
    /** The csh parameter set used by all languages in this collection */
    std::shared_ptr<CshParams> cshParams = std::make_shared<CshParams>();
    /** Adds a language. Returns false, if the language already added.*/
    bool AddLanguage(Chart::ChartFactoryFunction f, Csh::FileListProc proc);
    const LanguageData *GetLanguage(std::string_view language) const noexcept { auto i = std::find_if(languages.begin(), languages.end(), [language](const auto &l) {return l.GetName() == language; }); return i==languages.end() ? nullptr : &*i; } ///<Get the Language object based on language name.
    LanguageData *GetLanguageToModify(std::string_view language) noexcept { auto i = std::find_if(languages.begin(), languages.end(), [language](const auto &l) {return l.GetName() == language; }); return i==languages.end() ? nullptr : &*i; } ///<Get the Language object based on language name.
    const LanguageData* GetLanguageByExtension(std::string_view e) const noexcept { for (auto& l : languages) if (l.HasExtension(e)) return &l; return nullptr; } ///<Return the name of the language handling this extension. "" if none.
    const LanguageData *GetLanguageByDescr(std::string_view d) const noexcept { auto i = std::find_if(languages.begin(), languages.end(), [d](const auto &l) {return l.description == d; }); return i==languages.end() ? nullptr : &*i; } ///<Get the Language object based on language's short description.
    std::string LanguageForExtension(std::string_view e) const { for (auto &l : languages) if (l.HasExtension(e)) return l.GetName();  return std::string(); } ///<Return the name of the language handling this extension. "" if none.
    std::vector<std::string_view> GetLanguages() const { std::vector<std::string_view> ret; for (auto &l : languages) ret.push_back(l.GetName()); return ret; } ///<Get a list of supported languages.
    std::vector<std::string_view> GetExtensions() const { { std::vector<std::string_view> ret; for (auto &l : languages) ret.insert(ret.end(), l.extensions.begin(), l.extensions.end()); return ret; } } ///<Get a list of supported extensions.
    std::string GetLanguageNames() const { std::string langs; for (auto& l : languages) langs += l.GetName() + ", "; langs.pop_back(); langs.pop_back(); return langs; } ///<Provides a comma separated list of the libraries used.
    void AddLibraries(std::map<std::string, std::string>&& l) { for (auto& [name, license] : l) libraries.try_emplace(std::move(name), std::move(license)); }
    const std::map<std::string, std::string>& GetLibraries() const noexcept { return libraries; }
    std::string GetLibraryNames() const { std::string libs; for (auto &[s,_] : libraries) libs += s + ", "; libs.pop_back(); libs.pop_back(); return libs; } ///<Provides a comma separated list of the libraries used.
    MscError CompileDesignLibs(std::vector<std::pair<std::string, std::string>> &&design_files);
};


#endif