/*
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 commandline.h All that is needed to perform a command-line action.
* @ingroup libcgencommon_files  */

#if !defined(COMMANDLINE_H)
#define COMMANDLINE_H

#include <list>
#include <string>
#include <variant>
#include <unordered_map>
#include "version.h"
#include "cgen_attribute.h"
#include "cgencommon.h"

std::string VersionText(char a=LIBMSCGEN_MAJOR, char b = LIBMSCGEN_MINOR, char c = LIBMSCGEN_SUPERMINOR);
std::string ReadFile(FILE *in, bool till_zero=false, bool add_newline=true);
std::string version(const LanguageCollection &languages);
std::string license(const LanguageCollection &languages);

using LoadDataSet = std::map<std::string, std::string>;
/** Unpack the load data string by language type.*/
LoadDataSet SplitLoadData(std::string *load_data, MscError *Error=nullptr, const FileLineCol *opt_pos=nullptr);
/** Pack the updated load data back to a string.*/
std::string PackLoadData(const LoadDataSet &ldset);

/** One example for the example browser of the GUIs*/
struct Example {
    std::string name;         ///<The name of the example shown to the use
    std::string keywords;     ///<The keywords of the example separated by spaces
    std::string explanation;  ///<Descriptive text
    std::string text;         ///<The chart text of the example to compile
    /** Return a product of match levels for each search term
     * A search term gets 0 for no match to any keywords or the max of:
     * - 1 for partial match of a keyword,
     * - 2 when a keyword begins with the search term,
     * - 3 when the search term equals a keyword.
     * Then we multiply the scores of the search terms (aka AND relation).
     * Thus if we have a search term that gets 0, the whole search string will
     * not match at all (returns zero)*/
    int match(const std::vector<std::string_view>& search) const noexcept;
};

class Examples {
    const LanguageCollection* const L;
    std::unordered_map<const LanguageData*, std::vector<Example>> examples; //maps the language to a list of Examples
    const std::vector<Example> empty;
public:
    explicit Examples(const LanguageCollection* languages) noexcept : L(languages) {}
    /** Examine each of the example dirs and load the examples. If we find any examples
     * in a dir, we skip checking the rest of the dirs. A valid example is a file with a
     * chart text extension, where the first 3 lines start with a double hash-mark (##).*/
    std::pair<int, std::string> Load(const std::vector<std::string>&example_dirs, OpenNamedFileFunction *file_open_proc);
    /** Returns true of we have loaded at least one example for this language. */
    bool HasExamplesFor(const LanguageData* lang) const noexcept { return examples.find(lang)!=examples.end() && examples.find(lang)->second.size(); }
    /** Returns a ref to a list of examples we have loaded for this lang - or to an empty collection. */
    const std::vector<Example>& GetExamplesFor(const LanguageData* lang) const { auto i = examples.find(lang); return i==examples.end() ? empty : i->second; }
    /** Returns a list of examples of a language matching the search criteria. */
    std::vector<Example*> Filter(std::string_view search, const LanguageData* lang);
};


struct GUIInit {
    const LanguageCollection &languages;
    Chart::FileReadProcedure *file_read_proc = nullptr;
    void *file_read_proc_param = nullptr;
    OpenNamedFileFunction *file_open_proc = nullptr;
    std::string copyright_text;
    std::string file_to_open_path;
    std::string file_to_open_name;
    std::string type;
    std::string fontface, fontlang;
    std::map<std::string, std::vector<std::string_view>> command_line_arguments;
    std::vector<std::pair<Attribute, std::vector<std::string_view>>> command_line_options;
    std::optional<bool> pedantic;
    std::string start_chart_text;
    std::string forced_design;
    std::string *load_data = nullptr;
    double max_fps = 60;
};


std::variant<int, GUIInit>
do_main(const std::vector<std::string> &args,
        LanguageCollection &languages,
        std::vector<std::pair<std::string, std::string>> &&design_files,
        OpenNamedFileFunction ReadNamedFile,
        Chart::FileReadProcedure file_read_proc, void *file_read_proc_param,
        std::string csh_textformat,
        bool display_only = false,
        ProgressBase::ProgressCallback cb = nullptr, void *param = nullptr,
        std::string *load_data = nullptr);

/** Returns the text description of a reference, e.g., "Doxygen reference",
 * "Ctrl+Click to open in browser" or just "Reference" to be displayed in a
 * tooltip. If we also return true, the link can be given to a browser.*/
inline std::pair<std::string_view, bool> EvaluateReferenceForGUI(std::string_view ref) noexcept {
    if (CaseInsensitiveBeginsWith(ref, "\\ref "))
        return {"Doxygen reference", false};
    if (CaseInsensitiveBeginsWith(ref, "http://") ||
        CaseInsensitiveBeginsWith(ref, "https://") ||
        CaseInsensitiveBeginsWith(ref, "ftp://"))
        return {"Ctrl+Click to open in browser", true};
    return {"Reference", false};
}

#endif /* COMMANDLINE_H */
