/* FichothequeLib_Tools - Copyright (c) 2014-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.tools.corpus;

import java.util.ArrayList;
import java.util.List;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.FicheMeta;
import net.fichotheque.corpus.fiche.Fiche;
import net.fichotheque.corpus.fiche.FicheItem;
import net.fichotheque.corpus.fiche.Item;
import net.fichotheque.corpus.fiche.ParaItem;
import net.fichotheque.corpus.metadata.CorpusField;
import net.fichotheque.corpus.metadata.CorpusMetadata;
import net.fichotheque.corpus.metadata.FieldGeneration;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.extraction.ExtractionContext;
import net.fichotheque.format.FormatContext;
import net.fichotheque.format.FormatSource;
import net.fichotheque.format.formatters.SourceFormatter;
import net.fichotheque.pointeurs.FichePointeur;
import net.fichotheque.tools.format.FichothequeFormatDefEngine;
import net.fichotheque.utils.ExtractionUtils;
import net.fichotheque.utils.FormatterUtils;
import net.fichotheque.utils.pointeurs.PointeurFactory;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.localisation.LocalisationUtils;
import net.mapeadores.util.logging.SourceLogBuilder;
import net.mapeadores.util.text.StringUtils;


/**
 *
 * @author Vincent Calame
 */
public class FieldGenerationEngine {

    private final FormatContext formatContext;
    private final ExtractionContext extractionContext;
    private final Lang defaultLang;
    private final List<CorpusField> corpusFieldList = new ArrayList<CorpusField>();
    private final List<SourceFormatter> sourceFormatterList = new ArrayList<SourceFormatter>();

    private FieldGenerationEngine(ExtractionContext extractionContext, FormatContext formatContext, Lang defaultLang) {
        this.extractionContext = extractionContext;
        this.formatContext = formatContext;
        this.defaultLang = defaultLang;
    }

    private void add(CorpusField corpusField, SourceFormatter sourceFormatter) {
        corpusFieldList.add(corpusField);
        sourceFormatterList.add(sourceFormatter);
    }

    public boolean isEmpty() {
        return corpusFieldList.isEmpty();
    }

    public void run(FicheMeta ficheMeta, Fiche fiche) {
        Lang ficheLang = fiche.getLang();
        if (ficheLang == null) {
            ficheLang = defaultLang;
        }
        FichePointeur fichePointeur = PointeurFactory.toFichePointeur(ficheMeta, fiche);
        FormatSource formatSource = FormatterUtils.toFormatSource(fichePointeur, ExtractionUtils.derive(extractionContext, LocalisationUtils.toUserLangContext(ficheLang)), null, formatContext);
        int length = corpusFieldList.size();
        for (int i = 0; i < length; i++) {
            CorpusField corpusField = corpusFieldList.get(i);
            SourceFormatter sourceFormatter = sourceFormatterList.get(i);
            String result = sourceFormatter.formatSource(formatSource);
            switch (corpusField.getFieldString()) {
                case FieldKey.SPECIAL_TITLE: {
                    if (result == null) {
                        fiche.setTitle("");
                    } else {
                        fiche.setTitle(StringUtils.cleanString(result));
                    }
                    break;
                }
                case FieldKey.SPECIAL_SUBTITLE: {
                    if (result == null) {
                        fiche.setSubtitle(null);
                    } else {
                        fiche.setSubtitle(toPara(result));
                    }
                    break;
                }
                default:
                    if (corpusField.isProp()) {
                        fiche.setProp(corpusField.getFieldKey(), toFicheItem(corpusField.getFicheItemType(), result));
                    }
            }
        }
    }


    public static FieldGenerationEngine build(Corpus corpus, ExtractionContext extractionContext, FormatContext formatContext, Lang defaultLang) {
        FieldGenerationEngine fieldGenerationEngine = new FieldGenerationEngine(extractionContext, formatContext, defaultLang);
        CorpusMetadata corpusMetadata = corpus.getCorpusMetadata();
        FichothequeFormatDefEngine.Parameters parameters = FichothequeFormatDefEngine.parameters(formatContext);
        FieldGeneration fieldGeneration = corpus.getCorpusMetadata().getFieldGeneration();
        for (FieldGeneration.Entry entry : fieldGeneration.getEntryList()) {
            CorpusField corpusField = corpusMetadata.getCorpusField(entry.getFieldKey());
            if (corpusField != null) {
                SourceLogBuilder logBuilder = new SourceLogBuilder();
                SourceFormatter formatter = FichothequeFormatDefEngine.compute(corpus, entry.getFormatDef(), logBuilder, parameters);
                if (formatter == null) {
                    formatter = new ErrorSourceFormatter("#" + logBuilder.toRawString() + "#");
                }
                fieldGenerationEngine.add(corpusField, formatter);
            }
        }
        return fieldGenerationEngine;
    }

    private static FicheItem toFicheItem(String ficheItemType, String text) {
        if (text == null) {
            return null;
        }
        switch (ficheItemType) {
            case CorpusField.PARA_FIELD:
                return toPara(text);
            case CorpusField.ITEM_FIELD:
            default:
                return new Item(text);
        }
    }

    private static ParaItem toPara(String text) {
        ParaItem.Builder builder = new ParaItem.Builder();
        builder.addText(text);
        return builder.toParaItem();
    }


    private static class ErrorSourceFormatter implements SourceFormatter {

        private final String error;

        private ErrorSourceFormatter(String error) {
            this.error = error;
        }

        @Override
        public String formatSource(FormatSource formatSource) {
            return error;
        }

    }

}
