/* BdfServer - Copyright (c) 2022-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.tools.exportation.transformation.defaulttemplates;

import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.api.managers.UiManager;
import fr.exemole.bdfserver.api.namespaces.FicheFormSpace;
import fr.exemole.bdfserver.api.ui.CommentUi;
import fr.exemole.bdfserver.api.ui.DataUi;
import fr.exemole.bdfserver.api.ui.FieldUi;
import fr.exemole.bdfserver.api.ui.SubsetIncludeUi;
import fr.exemole.bdfserver.tools.exportation.transformation.BdfTransformationUtils;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import net.fichotheque.corpus.metadata.CorpusField;
import net.fichotheque.corpus.metadata.CorpusMetadata;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.namespaces.TransformationSpace;
import net.fichotheque.xml.extraction.TagNames;
import net.mapeadores.util.attr.Attribute;
import net.mapeadores.util.xml.XsltWriter;


/**
 *
 * @author Vincent Calame
 */
public class AbstractFicheXsltWriter extends XsltWriter {

    private static TransformationVariant SECTION_VARIANT = new TransformationVariant("component-Section");
    private static TransformationVariant FIELD_VARIANT = new TransformationVariant("component-FieldReference");
    protected final BdfServer bdfServer;
    protected final UiManager uiManager;
    protected final int extractVersion;
    protected final boolean uniqueLang;
    protected final TagNames tagNames;

    public AbstractFicheXsltWriter(BdfServer bdfServer, int extractVersion) {
        this.bdfServer = bdfServer;
        this.uiManager = bdfServer.getUiManager();
        this.extractVersion = extractVersion;
        this.uniqueLang = BdfTransformationUtils.isUniquelang(bdfServer);
        this.tagNames = new TagNames(extractVersion);
    }

    public void appendCommentUi(CommentUi commentUi) throws IOException {
        if (!commentUi.isTemplate()) {
            return;
        }
        Attribute condition = commentUi.getAttributes().getAttribute(FicheFormSpace.CONDITION_ATTRIBUTEKEY);
        boolean conditionDone = false;
        if (condition != null) {
            String firstValue = condition.getFirstValue().replace('\'', '!');
            if (firstValue.startsWith("~")) {
                openXslIf("*[contains(@groups, '" + firstValue + "')]", true);
                appendCommentUiCall(commentUi);
                closeXslIf(true);
                conditionDone = true;
            }
        }
        if (!conditionDone) {
            appendCommentUiCall(commentUi);
        }
        addSeparator();
    }

    private void appendCommentUiCall(CommentUi commentUi) throws IOException {
        openXslCallTemplate("component-addRawText", true);
        if (commentUi.isForm()) {
            addXslWithParam("param_ComponentName", commentUi.getCloneName(), true);
        }
        addXslWithParam("param_Text", "$label[@name='" + commentUi.getName() + "']", false);
        closeXslCallTemplate(true);
    }

    public void appendFieldUi(FieldUi fieldUi, CorpusMetadata corpusMetadata) throws IOException {
        if (!acceptUi(fieldUi)) {
            return;
        }
        FieldKey fieldKey = fieldUi.getFieldKey();
        CorpusField corpusField = corpusMetadata.getCorpusField(fieldKey);
        String mode = "component-Inline";
        String select = null;
        Map<String, String> paramMap = null;
        switch (fieldKey.getCategory()) {
            case FieldKey.PROP_CATEGORY:
                select = tagNames.getPropTagName() + "[@name='" + fieldKey.getFieldName() + "']";
                break;
            case FieldKey.SECTION_CATEGORY:
                select = "section[@name='" + fieldKey.getFieldName() + "']";
                TransformationVariant variant = getSectionVariant(fieldUi);
                mode = variant.getMode();
                paramMap = variant.getParamMap();
                break;
            case FieldKey.INFO_CATEGORY:
                select = tagNames.getInfoTagName() + "[@name='" + fieldKey.getFieldName() + "']";
                mode = DefaultTemplateUtils.getMode(corpusField);
                break;
            case FieldKey.SPECIAL_CATEGORY:
                switch (fieldKey.getKeyString()) {
                    case FieldKey.SPECIAL_LANG:
                        select = "lang";
                        break;
                    case FieldKey.SPECIAL_OWNERS:
                        select = tagNames.getOwnersTagName();
                        break;
                }
                break;
        }
        if (select != null) {
            apply(select, mode, fieldUi.getFieldString(), paramMap);
        }
    }


    public void appendSubsetIncludeUi(SubsetIncludeUi includeUi) throws IOException {
        String select = tagNames.getSubsetTagName(includeUi.getCategory()) + "[@name='" + includeUi.getName() + "']";
        String mode = DefaultTemplateUtils.getMode(includeUi);
        apply(select, mode, includeUi.getName());
    }

    public void appendDataUi(DataUi dataUi) throws IOException {
        apply("data[@name='" + dataUi.getDataName() + "']", "component-Listitem", dataUi.getName());
    }

    public void appendChrono() throws IOException {
        openXslApplyTemplates(".", "component-Chrono", true);
        addXslWithParam("param_CreationLabel", "$label[@name='date_creation']", false);
        addXslWithParam("param_ModificationLabel", "$label[@name='date_modification']", false);
        closeXslApplyTemplates(true);
        addSeparator();
    }

    public void apply(String match, String mode) throws IOException {
        appendIndent();
        addXslApplyTemplates(match, mode);
    }

    public void apply(String select, String mode, String labelName) throws IOException {
        apply(select, mode, labelName, null);
    }

    public void apply(String select, String mode, String labelName, Map<String, String> paramMap) throws IOException {
        openXslApplyTemplates(select, mode, true);
        addXslWithParam("param_Label", "$label[@name='" + labelName + "']", false);
        if (paramMap != null) {
            for (Map.Entry<String, String> entry : paramMap.entrySet()) {
                addXslWithParam(entry.getKey(), entry.getValue(), true);
            }
        }
        closeXslApplyTemplates(true);
        addSeparator();
    }

    public boolean acceptUi(FieldUi fieldUi) {
        switch (fieldUi.getFieldString()) {
            case FieldKey.SPECIAL_TITLE:
            case FieldKey.SPECIAL_SUBTITLE:
                return false;
            case FieldKey.SPECIAL_LANG:
                return (!uniqueLang);
            default:
                return true;
        }
    }

    private static TransformationVariant getSectionVariant(FieldUi fieldUi) {
        Attribute attribute = fieldUi.getAttributes().getAttribute(TransformationSpace.VARIANT_KEY);
        if (attribute != null) {
            switch (attribute.getFirstValue()) {
                case "heading":
                    Map<String, String> paramMap = null;
                    int size = attribute.size();
                    if (size > 1) {
                        paramMap = new LinkedHashMap<String, String>();
                        for (int i = 1; i < size; i++) {
                            String value = attribute.get(i);
                            if (value.startsWith("class=")) {
                                String classValue = value.substring("class=".length());
                                paramMap.put("param_HeadingClass", classValue);
                            }
                        }
                    }
                    return new TransformationVariant("component-Heading", paramMap);
                case "field":
                    return FIELD_VARIANT;
            }
        }
        return SECTION_VARIANT;
    }


    private static class TransformationVariant {

        private final String mode;
        private final Map<String, String> paramMap;

        private TransformationVariant(String mode) {
            this.mode = mode;
            this.paramMap = null;
        }

        private TransformationVariant(String mode, Map<String, String> paramMap) {
            this.mode = mode;
            this.paramMap = paramMap;
        }

        private String getMode() {
            return mode;
        }

        private Map<String, String> getParamMap() {
            return paramMap;
        }

    }

}
