/* BdfServer_Html - Copyright (c) 2018-2025 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package fr.exemole.bdfserver.html;

import fr.exemole.bdfserver.api.instruction.BdfParameters;
import fr.exemole.bdfserver.html.jslib.JsLibCatalog;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import net.fichotheque.Fichotheque;
import net.fichotheque.FichothequeConstants;
import net.fichotheque.addenda.Addenda;
import net.fichotheque.album.Album;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.metadata.FichothequeMetadata;
import net.fichotheque.permission.PermissionSummary;
import net.fichotheque.sphere.Sphere;
import net.fichotheque.thesaurus.Thesaurus;
import net.fichotheque.utils.FichothequeUtils;
import net.mapeadores.util.html.HA;
import net.mapeadores.util.html.HtmlPrinter;
import net.mapeadores.util.html.PageHtmlPrinter;
import net.mapeadores.util.html.TrustedHtml;
import net.mapeadores.util.io.DocStream;
import net.mapeadores.util.io.ResourceStorages;
import net.mapeadores.util.jslib.JsAnalyser;
import net.mapeadores.util.jslib.JsLibAssembly;
import net.mapeadores.util.jslib.TemplateFamily;
import net.mapeadores.util.jslib.ThirdLib;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.localisation.LocalisationUtils;
import net.mapeadores.util.text.Phrase;
import net.mapeadores.util.text.RelativePath;


/**
 *
 * @author Vincent Calame
 */
public final class BdfJsLibAssembly extends JsLibAssembly {

    private final static RelativePath BDFCORE = RelativePath.build("js/bdf/Bdf.js");
    private final JsAnalyser jsAnalyser;
    private final Set<String> messageKeySet = new LinkedHashSet();
    private String mainStorageKey = null;

    public BdfJsLibAssembly(JsAnalyser jsAnalyser) {
        this.jsAnalyser = jsAnalyser;
        messageKeySet.addAll(jsAnalyser.getJsMessageKeySet(BDFCORE));
    }

    public void setMainStorageKey(String mainStorageKey) {
        this.mainStorageKey = mainStorageKey;
    }

    public void resolve(PageHtmlPrinter htmlPrinter, String resourcePath, String suffix) {
        htmlPrinter
                .addJsUrl(resourcePath + "third-lib/jquery/jquery.js" + suffix);
        ThirdLib jqueryThirdLib = getThirdLib(JsLibCatalog.JQUERY);
        if (jqueryThirdLib != null) {
            resolvejQueryExtensions(jqueryThirdLib, htmlPrinter, resourcePath, suffix);
        }
        if (hasTemplate()) {
            htmlPrinter
                    .addJsUrl(resourcePath + "third-lib/jsrender/jsrender.min.js" + suffix);
        }
        ThirdLib codemirrorThirdLib = getThirdLib(JsLibCatalog.CODEMIRROR);
        if (codemirrorThirdLib != null) {
            htmlPrinter
                    .addJsUrl(resourcePath + "third-lib/codemirror/lib/codemirror.js" + suffix)
                    .addCssUrl(resourcePath + "third-lib/codemirror/lib/codemirror.css" + suffix);
            for (String codemirrorMode : codemirrorThirdLib.getExtensionNameList()) {
                if (isBdfMode(codemirrorMode)) {
                    htmlPrinter
                            .addJsUrl(resourcePath + "js/codemirror/" + codemirrorMode + ".js" + suffix);
                } else {
                    htmlPrinter
                            .addJsUrl(resourcePath + "third-lib/codemirror/mode/" + codemirrorMode + "/" + codemirrorMode + ".js" + suffix);
                }
            }
        }
        ThirdLib leafletThirdLib = getThirdLib(JsLibCatalog.LEAFLET);
        if (leafletThirdLib != null) {
            htmlPrinter
                    .addJsUrl(resourcePath + "third-lib/leaflet/leaflet.js" + suffix)
                    .addCssUrl(resourcePath + "third-lib/leaflet/leaflet.css" + suffix);
            for (String leafletExtension : leafletThirdLib.getExtensionNameList()) {
                htmlPrinter
                        .addJsUrl(resourcePath + "third-lib/leaflet/leaflet." + leafletExtension + ".js" + suffix)
                        .addCssUrl(resourcePath + "third-lib/leaflet/leaflet." + leafletExtension + ".css" + suffix);
            }
        }
        ThirdLib mergelyThirdLib = getThirdLib(JsLibCatalog.MERGELY);
        if (mergelyThirdLib != null) {
            htmlPrinter
                    .addJsUrl(resourcePath + "third-lib/mergely/mergely.js" + suffix)
                    .addCssUrl(resourcePath + "third-lib/mergely/mergely.css" + suffix);
        }
        ThirdLib jsrenderThirdLib = getThirdLib(JsLibCatalog.JSRENDER);
        if (jsrenderThirdLib != null) {
            htmlPrinter
                    .addJsUrl(resourcePath + "third-lib/jsrender/jsrender.min.js" + suffix);
        }
        htmlPrinter
                .addJsUrl(resourcePath + "js/overlay/Overlay.js" + suffix)
                .addJsUrl(resourcePath + BDFCORE.toString() + suffix);
        for (RelativePath relativePath : getScriptPaths()) {
            htmlPrinter
                    .addJsUrl(resourcePath + relativePath.toString() + suffix);
        }
    }

    public void resolvejQueryExtensions(ThirdLib jqueryThirdLib, PageHtmlPrinter htmlPrinter, String resourcePath, String suffix) {
        for (String jqueryExtensionName : jqueryThirdLib.getExtensionNameList()) {
            int idx = jqueryExtensionName.indexOf('-');
            String variant = "";
            if (idx > 0) {
                variant = jqueryExtensionName.substring(idx + 1);
                jqueryExtensionName = jqueryExtensionName.substring(0, idx);
            }
            htmlPrinter
                    .addJsUrl(resourcePath + "third-lib/jquery/jquery." + jqueryExtensionName + ".js" + suffix);
            switch (jqueryExtensionName) {
                case "tablesorter":
                    htmlPrinter
                            .addCssUrl(resourcePath + "third-lib/jquery/tablesorter/minimal.css" + suffix);
                    if (!variant.isEmpty()) {
                        htmlPrinter
                                .addCssUrl(resourcePath + "third-lib/jquery/tablesorter/theme." + variant + ".min.css" + suffix);
                    }
                    break;
            }
        }
    }

    public void end(HtmlPrinter hp, ResourceStorages resourceStorages, Fichotheque fichotheque, BdfParameters bdfParameters, Lang workingLang) {
        for (RelativePath relativePath : getScriptPaths()) {
            messageKeySet.addAll(jsAnalyser.getJsMessageKeySet(relativePath));
        }
        hp
                .__breakLine()
                .SCRIPT()
                .__newLine();
        if (fichotheque != null) {
            FichothequeMetadata fichothequeMetadata = fichotheque.getFichothequeMetadata();
            hp
                    .__escape("Bdf.BASE_URI=")
                    .__scriptLiteral(fichothequeMetadata.getAuthority() + "/" + fichothequeMetadata.getBaseName())
                    .__scriptLineEnd();
        }
        if (mainStorageKey != null) {
            hp
                    .__escape("Bdf.MAINSTORAGE_KEY=")
                    .__scriptLiteral(mainStorageKey)
                    .__scriptLineEnd();
        }
        hp
                .__escape("Bdf.WORKING_LANG=")
                .__scriptLiteral(workingLang.toString())
                .__scriptLineEnd()
                ._SCRIPT();
        hp
                .__(insertTemplates(hp, resourceStorages));
        hp
                .SCRIPT()
                .__escape("Bdf.Loc.add({")
                .__newLine();
        boolean next = false;
        for (String messageKey : messageKeySet) {
            switch (messageKey) {
                case "_ @title_corpus@": {
                    if (bdfParameters != null) {
                        PermissionSummary permissionSummary = bdfParameters.getPermissionSummary();
                        for (Corpus corpus : fichotheque.getCorpusList()) {
                            if (permissionSummary.hasAccess(corpus)) {
                                insertLoc(hp, "subset:" + corpus.getSubsetKeyString(), FichothequeUtils.getTitle(corpus, workingLang), next);
                                next = true;
                            }
                        }
                    }
                    break;
                }
                case "_ @title_thesaurus@": {
                    if (bdfParameters != null) {
                        PermissionSummary permissionSummary = bdfParameters.getPermissionSummary();
                        for (Thesaurus thesaurus : fichotheque.getThesaurusList()) {
                            if (permissionSummary.hasAccess(thesaurus)) {
                                insertLoc(hp, "subset:" + thesaurus.getSubsetKeyString(), FichothequeUtils.getTitle(thesaurus, workingLang), next);
                                next = true;
                            }
                        }
                    }
                    break;
                }
                case "_ @title_addenda@": {
                    if (bdfParameters != null) {
                        PermissionSummary permissionSummary = bdfParameters.getPermissionSummary();
                        for (Addenda addenda : fichotheque.getAddendaList()) {
                            if (permissionSummary.hasAccess(addenda)) {
                                insertLoc(hp, "subset:" + addenda.getSubsetKeyString(), FichothequeUtils.getTitle(addenda, workingLang), next);
                                next = true;
                            }
                        }
                    }
                    break;
                }
                case "_ @title_album@": {
                    if (bdfParameters != null) {
                        PermissionSummary permissionSummary = bdfParameters.getPermissionSummary();
                        for (Album album : fichotheque.getAlbumList()) {
                            if (permissionSummary.hasAccess(album)) {
                                insertLoc(hp, "subset:" + album.getSubsetKeyString(), FichothequeUtils.getTitle(album, workingLang), next);
                                next = true;
                            }
                        }
                    }
                    break;
                }
                case "_ @title_sphere@": {
                    if (bdfParameters != null) {
                        PermissionSummary permissionSummary = bdfParameters.getPermissionSummary();
                        for (Sphere sphere : fichotheque.getSphereList()) {
                            if (permissionSummary.hasAccess(sphere)) {
                                insertLoc(hp, "subset:" + sphere.getSubsetKeyString(), FichothequeUtils.getTitle(sphere, workingLang), next);
                                next = true;
                            }
                        }
                    }
                    break;
                }
                case "_ @phrases@": {
                    if (fichotheque != null) {
                        String title = fichotheque.getFichothequeMetadata().getTitleLabels().seekLabelString(workingLang, null);
                        if (title != null) {
                            insertLoc(hp, "fichotheque", title, next);
                            next = true;
                        }
                        for (Phrase phrase : fichotheque.getFichothequeMetadata().getPhrases()) {
                            String key = "phrase:" + phrase.getName();
                            String phraseTitle = phrase.seekLabelString(workingLang, null);
                            if (phraseTitle != null) {
                                insertLoc(hp, key, phraseTitle, next);
                                next = true;
                            }
                        }
                    }
                    break;
                }
                case "_ @corpus_newfiche@": {
                    if (bdfParameters != null) {
                        PermissionSummary permissionSummary = bdfParameters.getPermissionSummary();
                        for (Corpus corpus : fichotheque.getCorpusList()) {
                            if (permissionSummary.hasAccess(corpus)) {
                                String newFicheLabel = FichothequeUtils.getPhraseLabel(corpus.getCorpusMetadata().getPhrases(), FichothequeConstants.NEWFICHE_PHRASE, workingLang);
                                if (newFicheLabel != null) {
                                    insertLoc(hp, "newfiche:" + corpus.getSubsetKeyString(), newFicheLabel, next);
                                    next = true;
                                }
                            }
                        }
                    }
                    break;
                }
                default: {
                    String loc = hp.getMessageLocalisation().toString(messageKey);
                    if (loc != null) {
                        insertLoc(hp, messageKey, loc, next);
                        next = true;
                    }
                }
            }
        }
        hp
                .__newLine()
                .__escape("});")
                ._SCRIPT();
    }


    private boolean insertTemplates(HtmlPrinter hp, ResourceStorages resourceStorages) {
        for (TemplateFamily templateFamily : getTemplates()) {
            String familyName = templateFamily.getName();
            Set<String> existingSet = new HashSet<String>();
            RelativePath familyPath = templateFamily.getPath();
            RelativePath customFamilyPath = RelativePath.build("custom/" + familyPath.getPath());
            SortedMap<String, RelativePath> customMap = resourceStorages.listResources(customFamilyPath, true);
            if (!customMap.isEmpty()) {
                insertTemplates(hp, resourceStorages, familyName, customMap, existingSet);
            }
            SortedMap<String, RelativePath> map = resourceStorages.listResources(familyPath, true);
            insertTemplates(hp, resourceStorages, familyName, map, existingSet);
        }
        return true;
    }

    private void insertTemplates(HtmlPrinter hp, ResourceStorages resourceStorages, String familyName, SortedMap<String, RelativePath> map, Set<String> existingSet) {
        for (Map.Entry<String, RelativePath> entry : map.entrySet()) {
            String name = entry.getKey();
            if ((name.endsWith(".html")) && (!existingSet.contains(name))) {
                existingSet.add(name);
                name = name.substring(0, name.length() - 5);
                String content = "";
                DocStream docStream = resourceStorages.getResourceDocStream(entry.getValue());
                if (docStream != null) {
                    content = docStream.getContent();
                    messageKeySet.addAll(LocalisationUtils.scanMessageKeys(content));
                }
                String templateName;
                if (familyName.isEmpty()) {
                    templateName = name;
                } else {
                    templateName = familyName + ":" + name;
                }
                hp
                        .SCRIPT(HA.attr("data-name", templateName).type("text/x-jsrender"))
                        .__append(new TrustedHtml(content))
                        ._SCRIPT();
            }
        }
    }


    private void insertLoc(HtmlPrinter hp, String name, String value, boolean next) {
        if (next) {
            hp
                    .__escape(',')
                    .__newLine();
        }
        hp
                .__scriptLiteral(name)
                .__escape(':')
                .__scriptLiteral(value);
    }

    private static boolean isBdfMode(String mode) {
        switch (mode) {
            case "attributes":
            case "ficheblock":
            case "subsettree":
            case "tableexport":
            case "uicomponents":
                return true;
            default:
                return false;
        }
    }

}
