/* BdfServer_JsonProducers - Copyright (c) 2016-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.jsonproducers.pioche;

import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.api.interaction.domains.PiocheDomain;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.metadata.CorpusField;
import net.fichotheque.corpus.metadata.CorpusMetadata;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.utils.Comparators;
import net.fichotheque.utils.FichothequeUtils;
import net.mapeadores.util.conditions.ConditionsConstants;
import net.mapeadores.util.conditions.ConditionsUtils;
import net.mapeadores.util.conditions.TextCondition;
import net.mapeadores.util.conditions.TextTestEngine;
import net.mapeadores.util.json.JSONWriter;
import net.mapeadores.util.json.JsonProducer;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.localisation.LangPreference;
import net.mapeadores.util.text.Label;


/**
 *
 * @author Vincent Calame
 */
public class FieldArrayJsonProducer implements JsonProducer {

    private final BdfServer bdfServer;
    private final LangPreference langPreference;
    private final String type;
    private final SortedMap<FieldKey, FieldResult> resultMap = new TreeMap<FieldKey, FieldResult>(Comparators.FIELDKEY);

    public FieldArrayJsonProducer(BdfServer bdfServer, LangPreference langPreference, String type) {
        this.bdfServer = bdfServer;
        this.langPreference = langPreference;
        this.type = type;
    }

    public void init(TextCondition condition) {
        CorpusFieldTest corpusFieldTest;
        if (condition != null) {
            switch (ConditionsUtils.getConditionType(condition)) {
                case ConditionsConstants.PARTIAL_CONDITION:
                case ConditionsConstants.PARTIALOREMPTY_CONDITION:
                    corpusFieldTest = new PartialCorpusFieldTest(condition, langPreference);
                    break;
                case ConditionsConstants.EMPTY_CONDITION:
                case ConditionsConstants.IMPOSSIBLE_CONDITION:
                    corpusFieldTest = new BooleanCorpusFieldTest(false);
                    break;
                default:
                    corpusFieldTest = new BooleanCorpusFieldTest(true);
            }
        } else {
            corpusFieldTest = new BooleanCorpusFieldTest(true);
        }
        switch (type) {
            case PiocheDomain.DATE_FIELDTYPE:
                testDateFields(corpusFieldTest);
                break;
            default:
                testAllFields(corpusFieldTest);
                break;
        }
    }

    @Override
    public void writeJson(Appendable appendable) throws IOException {
        Lang lang = langPreference.getFirstLang();
        JSONWriter jw = new JSONWriter(appendable);
        jw.object();
        {
            jw.key("count")
                    .value(resultMap.size());
            jw.key("array");
            jw.array();
            for (Map.Entry<FieldKey, FieldResult> entry : resultMap.entrySet()) {
                jw.object();
                {
                    jw.key("code")
                            .value(entry.getKey().getKeyString());
                    jw.key("title")
                            .value(entry.getValue().getText(lang));
                }
                jw.endObject();
            }
            jw.endArray();
        }
        jw.endObject();
    }

    private void testDateFields(CorpusFieldTest corpusFieldTest) {
        for (Corpus corpus : bdfServer.getFichotheque().getCorpusList()) {
            CorpusMetadata corpusMetadata = corpus.getCorpusMetadata();
            for (CorpusField corpusField : corpusMetadata.getPropList()) {
                if (corpusField.isType(CorpusField.DATE_FIELD)) {
                    testField(corpusFieldTest, corpusField);
                }
            }
            for (CorpusField corpusField : corpusMetadata.getInfoList()) {
                if (corpusField.isType(CorpusField.DATE_FIELD)) {
                    testField(corpusFieldTest, corpusField);
                }
            }
        }
    }

    private void testAllFields(CorpusFieldTest corpusFieldTest) {
        boolean withSubtitle = false;
        for (Corpus corpus : bdfServer.getFichotheque().getCorpusList()) {
            CorpusMetadata corpusMetadata = corpus.getCorpusMetadata();
            testField(corpusFieldTest, corpusMetadata.getCorpusField(FieldKey.TITLE));
            testField(corpusFieldTest, corpusMetadata.getCorpusField(FieldKey.OWNERS));
            testField(corpusFieldTest, corpusMetadata.getCorpusField(FieldKey.LANG));
            for (CorpusField corpusField : corpusMetadata.getPropList()) {
                testField(corpusFieldTest, corpusField);
            }
            for (CorpusField corpusField : corpusMetadata.getInfoList()) {
                testField(corpusFieldTest, corpusField);
            }
            for (CorpusField corpusField : corpusMetadata.getSectionList()) {
                testField(corpusFieldTest, corpusField);
            }
            if (!withSubtitle) {
                CorpusField subtitleField = corpusMetadata.getCorpusField(FieldKey.SUBTITLE);
                if (subtitleField != null) {
                    testField(corpusFieldTest, subtitleField);
                }
            }
        }
    }

    private void testField(CorpusFieldTest corpusFieldTest, CorpusField corpusField) {
        CorpusMetadata corpusMetadata = corpusField.getCorpusMetadata();
        if (corpusFieldTest.testFieldKey(corpusField)) {
            FieldResult fieldResult = getFieldResult(corpusField);
            Label label = corpusField.getLabels().getLangPartCheckedLabel(langPreference.getFirstLang());
            if (label != null) {
                fieldResult.addLabel(label.getLabelString(), corpusMetadata);
            } else {
                fieldResult.addLabel("", corpusMetadata);
            }
        } else {
            Label label = corpusFieldTest.testLabel(corpusField);
            if (label != null) {
                FieldResult fieldResult = getFieldResult(corpusField);
                fieldResult.addLabel(label.getLabelString(), corpusMetadata);
            }
        }
    }

    private FieldResult getFieldResult(CorpusField corpusField) {
        FieldKey fieldKey = corpusField.getFieldKey();
        FieldResult fieldResult = resultMap.get(fieldKey);
        if (fieldResult == null) {
            fieldResult = new FieldResult(fieldKey);
            resultMap.put(fieldKey, fieldResult);
        }
        return fieldResult;
    }


    private class FieldResult {

        private final FieldKey fieldKey;
        private final Map<String, CorpusByLabel> labelMap = new LinkedHashMap<String, CorpusByLabel>();

        private FieldResult(FieldKey fieldKey) {
            this.fieldKey = fieldKey;
        }

        private void addLabel(String labelText, CorpusMetadata corpusMetadata) {
            CorpusByLabel corpusByLabel = labelMap.get(labelText);
            if (corpusByLabel == null) {
                corpusByLabel = new CorpusByLabel();
                labelMap.put(labelText, corpusByLabel);
            }
            corpusByLabel.addCorpus(corpusMetadata);
        }

        private String getText(Lang lang) {
            StringBuilder buf = new StringBuilder();
            for (Map.Entry<String, CorpusByLabel> entry : labelMap.entrySet()) {
                if (buf.length() > 0) {
                    buf.append(" / ");
                }
                buf.append(entry.getKey());
                buf.append(" (");
                entry.getValue().appendList(buf, lang);
                buf.append(")");
            }
            return buf.toString();
        }

    }


    private static class CorpusByLabel {

        private final List<CorpusMetadata> list = new ArrayList<CorpusMetadata>();

        private CorpusByLabel() {

        }

        private void addCorpus(CorpusMetadata corpusMetadata) {
            list.add(corpusMetadata);
        }

        private void appendList(StringBuilder buf, Lang lang) {
            boolean first = true;
            for (CorpusMetadata corpusMetadata : list) {
                if (first) {
                    first = false;
                } else {
                    buf.append(", ");
                }
                buf.append(FichothequeUtils.getTitle(corpusMetadata.getCorpus(), lang));
            }
        }

    }


    private static abstract class CorpusFieldTest {

        protected abstract boolean testFieldKey(CorpusField corpusField);

        protected abstract Label testLabel(CorpusField corpusField);

    }


    private static class BooleanCorpusFieldTest extends CorpusFieldTest {

        private final boolean accept;

        private BooleanCorpusFieldTest(boolean accept) {
            this.accept = accept;
        }

        @Override
        protected boolean testFieldKey(CorpusField corpusField) {
            return accept;
        }

        @Override
        protected Label testLabel(CorpusField corpusField) {
            return null;
        }

    }


    private static class PartialCorpusFieldTest extends CorpusFieldTest {

        private final TextTestEngine keyTestEngine;
        private final TextTestEngine[] labelTestEngineArray;

        private PartialCorpusFieldTest(TextCondition condition, LangPreference langPreference) {
            this.keyTestEngine = TextTestEngine.newInstance(condition, Lang.build("en"));
            int langLength = langPreference.size();
            labelTestEngineArray = new TextTestEngine[langLength];
            for (int i = 0; i < langLength; i++) {
                Lang lang = langPreference.get(i);
                labelTestEngineArray[i] = TextTestEngine.newInstance(condition, lang);
            }
        }

        @Override
        protected boolean testFieldKey(CorpusField corpusField) {
            return keyTestEngine.isSelected(corpusField.getFieldString());
        }

        @Override
        protected Label testLabel(CorpusField corpusField) {
            for (TextTestEngine engine : labelTestEngineArray) {
                Lang lang = engine.getLang();
                Label fieldLabel = corpusField.getLabels().getLangPartCheckedLabel(lang);
                if (fieldLabel != null) {
                    boolean selected = engine.isSelected(fieldLabel.getLabelString());
                    return (selected) ? fieldLabel : null;
                }
            }
            return null;
        }


    }

}
