/* FichothequeLib_API - Copyright (c) 2010-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 net.fichotheque.utils;

import java.io.IOException;
import java.util.List;
import net.fichotheque.Metadata;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.selection.CroisementCondition;
import net.fichotheque.selection.DocumentQuery;
import net.fichotheque.selection.FicheCondition;
import net.fichotheque.selection.FicheQuery;
import net.fichotheque.selection.FichothequeQueries;
import net.fichotheque.selection.FieldContentCondition;
import net.fichotheque.selection.IllustrationQuery;
import net.fichotheque.selection.MotcleCondition;
import net.fichotheque.selection.MotcleQuery;
import net.fichotheque.selection.PeriodCondition;
import net.fichotheque.selection.RangeCondition;
import net.fichotheque.selection.SelectionOptions;
import net.fichotheque.selection.StatusCondition;
import net.fichotheque.selection.SubsetCondition;
import net.fichotheque.selection.UserCondition;
import net.mapeadores.util.annotation.Nullable;
import net.mapeadores.util.attr.AttributeUtils;
import net.mapeadores.util.conditions.ConditionsUtils;
import net.mapeadores.util.conditions.ConditionsXMLStorage;
import net.mapeadores.util.conditions.TextCondition;
import net.mapeadores.util.primitives.RangeUtils;
import net.mapeadores.util.text.LabelUtils;
import net.mapeadores.util.xml.XMLWriter;


/**
 *
 * @author Vincent Calame
 */
public final class FichothequeXMLUtils {

    private FichothequeXMLUtils() {
    }

    public static void write(Metadata metadata, XMLWriter writer) throws IOException {
        LabelUtils.addLabels(writer, metadata.getTitleLabels());
        LabelUtils.addPhrases(writer, metadata.getPhrases());
        AttributeUtils.addAttributes(writer, metadata.getAttributes());
    }

    public static void writeSelectionOptions(XMLWriter xmlWriter, SelectionOptions selectionOptions) throws IOException {
        xmlWriter.addSimpleElement("selectiondef-name", selectionOptions.getSelectionDefName());
        writeFichothequeQueries(xmlWriter, selectionOptions.getCustomFichothequeQueries(), false);
    }

    public static void writeFichothequeQueries(XMLWriter xmlWriter, FichothequeQueries fichothequeQueries, boolean qVariant) throws IOException {
        for (FicheQuery ficheQuery : fichothequeQueries.getFicheQueryList()) {
            xmlWriter.openTag("fiche-query");
            FichothequeXMLUtils.writeFicheQuery(xmlWriter, ficheQuery, qVariant);
            xmlWriter.closeTag("fiche-query");
        }
        for (MotcleQuery motcleQuery : fichothequeQueries.getMotcleQueryList()) {
            xmlWriter.openTag("motcle-query");
            FichothequeXMLUtils.writeMotcleQuery(xmlWriter, motcleQuery, qVariant);
            xmlWriter.closeTag("motcle-query");
        }
    }

    public static void writeFicheQuery(XMLWriter xmlWriter, FicheQuery ficheQuery) throws IOException {
        writeFicheQuery(xmlWriter, ficheQuery, null, false, false);
    }

    public static void writeFicheQuery(XMLWriter xmlWriter, FicheQuery ficheQuery, boolean qVariant) throws IOException {
        writeFicheQuery(xmlWriter, ficheQuery, null, false, qVariant);
    }

    public static void writeFicheQuery(XMLWriter xmlWriter, FicheCondition.Entry ficheConditionEntry, boolean qVariant) throws IOException {
        writeFicheQuery(xmlWriter, ficheConditionEntry.getFicheQuery(), ficheConditionEntry.getCroisementCondition(), ficheConditionEntry.includeSatellites(), qVariant);
    }

    private static void writeFicheQuery(XMLWriter xmlWriter, FicheQuery ficheQuery, @Nullable CroisementCondition croisementCondition, boolean includeSattellites, boolean qVariant) throws IOException {
        SubsetCondition corpusCondition = ficheQuery.getCorpusCondition();
        if (corpusCondition.isExclude()) {
            xmlWriter.addEmptyElement("exclude");
        }
        for (SubsetKey subsetKey : corpusCondition.getSubsetKeySet()) {
            xmlWriter.addSimpleElement("corpus", subsetKey.getSubsetName());
        }
        if (corpusCondition.isWithCurrent()) {
            xmlWriter.addEmptyElement("current");
        }
        writeRangeCondition(xmlWriter, ficheQuery.getIdRangeCondition(), "range");
        String discardFilter = ficheQuery.getDiscardFilter();
        if (!discardFilter.equals(FicheQuery.DISCARDFILTER_ALL)) {
            xmlWriter.startOpenTag("discard");
            xmlWriter.addAttribute("filter", discardFilter);
            xmlWriter.closeEmptyTag();
        }
        writePeriodCondition(xmlWriter, ficheQuery.getPeriodCondition());
        writeFieldContentCondition(xmlWriter, ficheQuery.getFieldContentCondition(), qVariant);
        UserCondition userCondition = ficheQuery.getUserCondition();
        if (userCondition != null) {
            if (userCondition.isSome()) {
                xmlWriter.openTag("users");
                for (UserCondition.Entry entry : userCondition.getEntryList()) {
                    if (entry instanceof UserCondition.LoginEntry) {
                        UserCondition.LoginEntry loginEntry = (UserCondition.LoginEntry) entry;
                        xmlWriter.startOpenTag("user");
                        xmlWriter.addAttribute("sphere", loginEntry.getSphereName());
                        xmlWriter.addAttribute("login", loginEntry.getLogin());
                        xmlWriter.closeEmptyTag();
                    } else if (entry instanceof UserCondition.IdEntry) {
                        UserCondition.IdEntry idEntry = (UserCondition.IdEntry) entry;
                        xmlWriter.startOpenTag("user");
                        xmlWriter.addAttribute("sphere", idEntry.getSphereName());
                        xmlWriter.addAttribute("id", String.valueOf(idEntry.getId()));
                        xmlWriter.closeEmptyTag();
                    } else {
                        xmlWriter.addSimpleElement("sphere", entry.getSphereName());
                    }
                }
                xmlWriter.closeTag("users");
            } else {
                xmlWriter.startOpenTag("users");
                xmlWriter.addAttribute("filter", userCondition.getFilter());
                xmlWriter.closeEmptyTag();
            }
        }
        if (ficheQuery.isWithGeoloc()) {
            xmlWriter.addEmptyElement("geoloc");
        }
        writeMotcleCondition(xmlWriter, ficheQuery.getMotcleCondition(), qVariant);
        writeFicheCondition(xmlWriter, ficheQuery.getFicheCondition(), qVariant);
        if (includeSattellites) {
            xmlWriter.addEmptyElement("satellites");
        }
        if (croisementCondition != null) {
            writeCroisementCondition(xmlWriter, croisementCondition);
        }
    }

    private static void writeFieldContentCondition(XMLWriter xmlWriter, FieldContentCondition fieldContentCondition, boolean qVariant) throws IOException {
        if (fieldContentCondition == null) {
            return;
        }
        TextCondition textCondition = fieldContentCondition.getTextCondition();
        xmlWriter.startOpenTag("content");
        xmlWriter.addAttribute("scope", fieldContentCondition.getScope());
        if (qVariant) {
            xmlWriter.addAttribute("q", ConditionsUtils.conditionToString(textCondition));
        }
        xmlWriter.addAttribute("operator", textCondition.getLogicalOperator());
        if ((qVariant) && (!fieldContentCondition.isWithFieldKeyList())) {
            xmlWriter.closeEmptyTag();
        } else {
            xmlWriter.endOpenTag();
            if (fieldContentCondition.isWithFieldKeyList()) {
                for (FieldKey fieldKey : fieldContentCondition.getFieldKeyList()) {
                    xmlWriter.addSimpleElement("field", fieldKey.getKeyString());
                }
            }
            if (!qVariant) {
                ConditionsXMLStorage.appendTestText(xmlWriter, textCondition);
            }
            xmlWriter.closeTag("content");
        }
    }

    public static void writeMotcleQuery(XMLWriter xmlWriter, MotcleQuery motcleQuery) throws IOException {
        writeMotcleQuery(xmlWriter, motcleQuery, null, false, false);
    }

    public static void writeMotcleQuery(XMLWriter xmlWriter, MotcleQuery motcleQuery, boolean qVariant) throws IOException {
        writeMotcleQuery(xmlWriter, motcleQuery, null, false, qVariant);
    }

    public static void writeMotcleQuery(XMLWriter xmlWriter, MotcleCondition.Entry motcleConditionEntry, boolean qVariant) throws IOException {
        writeMotcleQuery(xmlWriter, motcleConditionEntry.getMotcleQuery(), motcleConditionEntry.getCroisementCondition(), motcleConditionEntry.isWithMaster(), qVariant);
    }

    private static void writeMotcleQuery(XMLWriter xmlWriter, MotcleQuery motcleQuery, @Nullable CroisementCondition croisementCondition, boolean withMaster, boolean qVariant) throws IOException {
        if (withMaster) {
            xmlWriter.addEmptyElement("master");
        }
        if (croisementCondition != null) {
            writeCroisementCondition(xmlWriter, croisementCondition);
        }
        SubsetCondition thesaurusCondition = motcleQuery.getThesaurusCondition();
        if (thesaurusCondition.isExclude()) {
            xmlWriter.addEmptyElement("exclude");
        }
        for (SubsetKey subsetKey : thesaurusCondition.getSubsetKeySet()) {
            xmlWriter.addSimpleElement("thesaurus", subsetKey.getSubsetName());
        }
        if (thesaurusCondition.isWithCurrent()) {
            xmlWriter.addEmptyElement("current");
        }
        writeRangeCondition(xmlWriter, motcleQuery.getIdRangeCondition(), "range");
        writeRangeCondition(xmlWriter, motcleQuery.getLevelRangeCondition(), "level");
        writeStatusCondition(xmlWriter, motcleQuery.getStatusCondition());
        writeContentCondition(xmlWriter, motcleQuery.getContentCondition(), qVariant);
        writeFicheCondition(xmlWriter, motcleQuery.getFicheCondition(), qVariant);
    }

    private static void writeContentCondition(XMLWriter xmlWriter, MotcleQuery.ContentCondition contentCondition, boolean qVariant) throws IOException {
        if (contentCondition == null) {
            return;
        }
        TextCondition textCondition = contentCondition.getTextCondition();
        String scope = contentCondition.getScope();
        xmlWriter.startOpenTag("content");
        if (!scope.equals(MotcleQuery.SCOPE_IDALPHA_ONLY)) {
            xmlWriter.addAttribute("scope", scope);
        }
        if (qVariant) {
            xmlWriter.addAttribute("q", ConditionsUtils.conditionToString(textCondition));
        }
        xmlWriter.addAttribute("operator", textCondition.getLogicalOperator());
        if (qVariant) {
            xmlWriter.closeEmptyTag();
        } else {
            xmlWriter.endOpenTag();
            ConditionsXMLStorage.appendTestText(xmlWriter, textCondition);
            xmlWriter.closeTag("content");
        }
    }

    public static void writeIllustrationQuery(XMLWriter xmlWriter, IllustrationQuery illustrationQuery) throws IOException {
        writeIllustrationQuery(xmlWriter, illustrationQuery, null);
    }

    public static void writeIllustrationQuery(XMLWriter xmlWriter, IllustrationQuery illustrationQuery, @Nullable CroisementCondition croisementCondition) throws IOException {
        for (SubsetKey subsetKey : illustrationQuery.getAlbumKeyList()) {
            xmlWriter.addSimpleElement("album", subsetKey.getSubsetName());
        }
        for (String albumDimName : illustrationQuery.getAlbumDimNameList()) {
            xmlWriter.addSimpleElement("albumdim", albumDimName);
        }
        if (croisementCondition != null) {
            writeCroisementCondition(xmlWriter, croisementCondition);
        }
    }

    public static void writeDocumentQuery(XMLWriter xmlWriter, DocumentQuery documentQuery) throws IOException {
        writeDocumentQuery(xmlWriter, documentQuery, null);
    }


    public static void writeDocumentQuery(XMLWriter xmlWriter, DocumentQuery documentQuery, @Nullable CroisementCondition croisementCondition) throws IOException {
        for (SubsetKey subsetKey : documentQuery.getAddendaKeyList()) {
            xmlWriter.addSimpleElement("addenda", subsetKey.getSubsetName());
        }
        if (croisementCondition != null) {
            writeCroisementCondition(xmlWriter, croisementCondition);
        }
    }

    private static void writeCroisementCondition(XMLWriter xmlWriter, CroisementCondition croisementCondition) throws IOException {
        if (croisementCondition != null) {
            List<String> modeList = croisementCondition.getLienModeList();
            for (String mode : modeList) {
                if (mode.isEmpty()) {
                    mode = "_default";
                }
                xmlWriter.addSimpleElement("mode", mode);
            }
            writeRangeCondition(xmlWriter, croisementCondition.getWeightRangeCondition(), "weight");
        }
    }

    private static void writePeriodCondition(XMLWriter xmlWriter, PeriodCondition periodCondition) throws IOException {
        if (periodCondition == null) {
            return;
        }
        xmlWriter.startOpenTag("period");
        xmlWriter.addAttribute("start", periodCondition.getStartString());
        String endString = periodCondition.getEndString();
        if (!endString.equals(PeriodCondition.SAME_TYPE)) {
            xmlWriter.addAttribute("end", endString);
        }
        xmlWriter.endOpenTag();
        if (periodCondition.isOnCreationDate()) {
            xmlWriter.addSimpleElement("chrono", "creation");
        }
        if (periodCondition.isOnModificationDate()) {
            xmlWriter.addSimpleElement("chrono", "modification");
        }
        for (FieldKey fieldKey : periodCondition.getFieldKeyList()) {
            xmlWriter.addSimpleElement("field", fieldKey.getKeyString());
        }
        xmlWriter.closeTag("period");
    }

    private static void writeRangeCondition(XMLWriter xmlWriter, RangeCondition rangeCondition, String tagName) throws IOException {
        if (rangeCondition == null) {
            return;
        }
        xmlWriter.startOpenTag(tagName);
        if (rangeCondition.isExclude()) {
            xmlWriter.addAttribute("exclude", "true");
        }
        xmlWriter.endOpenTag();
        xmlWriter.addText(RangeUtils.positiveRangesToString(rangeCondition.getRanges()));
        xmlWriter.closeTag(tagName, false);
    }

    private static void writeStatusCondition(XMLWriter xmlWriter, StatusCondition statusCondition) throws IOException {
        if (statusCondition == null) {
            return;
        }
        for (String status : statusCondition.getStatusSet()) {
            xmlWriter.addSimpleElement("status", status);
        }
    }

    private static void writeFicheCondition(XMLWriter xmlWriter, FicheCondition ficheCondition, boolean qVariant) throws IOException {
        if (ficheCondition == null) {
            return;
        }
        List<FicheCondition.Entry> entryList = ficheCondition.getEntryList();
        if (entryList.size() > 1) {
            xmlWriter.startOpenTag("fiche-query-logic");
            xmlWriter.addAttribute("operator", ficheCondition.getLogicalOperator());
            xmlWriter.closeEmptyTag();
        }
        for (FicheCondition.Entry ficheEntry : entryList) {
            xmlWriter.openTag("fiche-query");
            writeFicheQuery(xmlWriter, ficheEntry, qVariant);
            xmlWriter.closeTag("fiche-query");
        }
    }

    private static void writeMotcleCondition(XMLWriter xmlWriter, MotcleCondition motcleCondition, boolean qVariant) throws IOException {
        if (motcleCondition == null) {
            return;
        }
        List<MotcleCondition.Entry> entryList = motcleCondition.getEntryList();
        if (entryList.size() > 1) {
            xmlWriter.startOpenTag("motcle-query-logic");
            xmlWriter.addAttribute("operator", motcleCondition.getLogicalOperator());
            xmlWriter.closeEmptyTag();
        }
        for (MotcleCondition.Entry indexationEntry : entryList) {
            xmlWriter.openTag("motcle-query");
            writeMotcleQuery(xmlWriter, indexationEntry, qVariant);
            xmlWriter.closeTag("motcle-query");
        }
    }


}
