/* FichothequeLib_Xml - Copyright (c) 2006-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.xml.extraction;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.function.Predicate;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.FicheMeta;
import net.fichotheque.extraction.ExtractParameters;
import net.fichotheque.extraction.ExtractionContext;
import net.fichotheque.extraction.ExtractionSource;
import net.fichotheque.extraction.def.CorpusExtractDef;
import net.fichotheque.extraction.def.ExtractionDef;
import net.fichotheque.extraction.def.TagNameInfo;
import net.fichotheque.extraction.def.ThesaurusExtractDef;
import net.fichotheque.extraction.run.CorpusExtractResult;
import net.fichotheque.extraction.run.CorpusExtractor;
import net.fichotheque.extraction.run.ThesaurusExtractResult;
import net.fichotheque.extraction.run.ThesaurusExtractor;
import net.fichotheque.selection.FicheCondition;
import net.fichotheque.selection.FicheSelector;
import net.fichotheque.selection.MotcleCondition;
import net.fichotheque.selection.MotcleSelector;
import net.fichotheque.selection.SelectionContext;
import net.fichotheque.selection.SubsetCondition;
import net.fichotheque.thesaurus.Motcle;
import net.fichotheque.thesaurus.Thesaurus;
import net.fichotheque.utils.SelectionUtils;
import net.fichotheque.utils.selection.FicheSelectorBuilder;
import net.fichotheque.utils.selection.MotcleSelectorBuilder;
import net.fichotheque.utils.selection.SelectionContextBuilder;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.xml.XMLPart;
import net.mapeadores.util.xml.XMLWriter;


/**
 *
 * @author Vincent Calame
 */
public class ExtractionXMLPart extends XMLPart {

    private final static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
    private final ExtractParameters extractParameters;
    private final CorpusExtractXMLPart corpusExtractXMLPart;
    private final ThesaurusExtractXMLPart thesaurusExtractXMLPart;
    private final String dateString;
    private boolean allowsNoEnclosingTag = false;

    public ExtractionXMLPart(XMLWriter xmlWriter, ExtractParameters extractParameters) {
        super(xmlWriter);
        this.extractParameters = extractParameters;
        this.dateString = simpleDateFormat.format(new Date());
        corpusExtractXMLPart = new CorpusExtractXMLPart(xmlWriter, extractParameters);
        thesaurusExtractXMLPart = new ThesaurusExtractXMLPart(xmlWriter, extractParameters);
    }

    public void allowsNoEnclosingTag(boolean allowsNoEnclosingTag) {
        this.allowsNoEnclosingTag = allowsNoEnclosingTag;
    }

    public void startExtraction(ExtractionDef extractionDef) throws IOException {
        String tagName = getTagName(extractionDef.getTagNameInfo());
        if ((tagName == null) && (!allowsNoEnclosingTag)) {
            tagName = "NULL";
        }
        if (tagName != null) {
            startOpenTag(tagName);
            addAttribute("date", dateString);
            endOpenTag();
        }
    }

    public void addExtraction(ExtractionDef extractionDef, ExtractionSource extractionSource) throws IOException {
        Object source = extractionSource.getDynamicObject();
        if (source != null) {
            addDynamic(extractionDef, source);
        }
        addStatic(getStaticTagName(extractionDef.getStaticTagNameInfo()), extractionSource);
    }

    public ExtractionXMLPart endExtraction(ExtractionDef extractionDef) throws IOException {
        String tagName = getTagName(extractionDef.getTagNameInfo());
        if ((tagName == null) && (!allowsNoEnclosingTag)) {
            tagName = "NULL";
        }
        if (tagName != null) {
            closeTag(tagName);
        }
        return this;
    }

    private void addDynamic(ExtractionDef extractionDef, Object source) throws IOException {
        if (source instanceof FicheMeta) {
            addFicheMetaDynamic(extractionDef.getDynamicCorpusExtractDef(), (FicheMeta) source);
        } else if (source instanceof Corpus) {
            addCorpusDynamic(extractionDef, (Corpus) source);
        } else if (source instanceof Motcle) {
            addMotcleDynamic(extractionDef.getDynamicThesaurusExtractDef(), (Motcle) source);
        } else if (source instanceof Thesaurus) {
            addThesaurusDynamic(extractionDef.getDynamicThesaurusExtractDef(), (Thesaurus) source);
        } else {
            throw new ClassCastException("unable to deal with the class " + source.getClass().getName());
        }
    }

    private void addFicheMetaDynamic(CorpusExtractDef dynamicCorpusExtractDef, FicheMeta ficheMeta) throws IOException {
        if (dynamicCorpusExtractDef == null) {
            return;
        }
        CorpusExtractor corpusExtractor = extractParameters.getExtractionContext().getExtractorProvider().getCorpusExtractor(dynamicCorpusExtractDef);
        corpusExtractor.add(ficheMeta, null);
        corpusExtractXMLPart.addCorpusExtract(corpusExtractor.getCorpusExtractResult());
    }

    private void addCorpusDynamic(ExtractionDef extractionDef, Corpus corpus) throws IOException {
        ExtractionContext extractionContext = extractParameters.getExtractionContext();
        CorpusExtractDef dynamicCorpusExtractDef = extractionDef.getDynamicCorpusExtractDef();
        if (dynamicCorpusExtractDef == null) {
            return;
        }
        CorpusExtractor corpusExtractor = extractionContext.getExtractorProvider().getCorpusExtractor(dynamicCorpusExtractDef);
        List<FicheCondition.Entry> entryList = dynamicCorpusExtractDef.getConditionEntryList();
        if (!entryList.isEmpty()) {
            SubsetKey corpusKey = corpus.getSubsetKey();
            SubsetCondition singletonCondition = SelectionUtils.toSingletonSubsetCondition(corpusKey);
            List<FicheCondition.Entry> newEntryList = new ArrayList<FicheCondition.Entry>();
            for (FicheCondition.Entry entry : entryList) {
                if (entry.getFicheQuery().getCorpusCondition().accept(corpusKey)) {
                    newEntryList.add(SelectionUtils.toFicheConditionEntry(SelectionUtils.derive(entry.getFicheQuery(), singletonCondition), entry.getJunctionCondition(), entry.includeSatellites()));
                }
            }
            if (newEntryList.isEmpty()) {
                return;
            }
            Lang defaultLang = extractionContext.getLangContext().getDefaultLang();
            SelectionContext selectionContext = SelectionContextBuilder.build(extractionContext, defaultLang)
                    .setSubsetAccessPredicate(extractionContext.getSubsetAccessPredicate())
                    .setFichePredicate(extractParameters.getFichePredicate())
                    .toSelectionContext();
            FicheSelector ficheSelector = FicheSelectorBuilder.init(selectionContext).addAll(newEntryList).toFicheSelector();
            for (FicheMeta ficheMeta : corpus.getFicheMetaList()) {
                if (ficheSelector.test(ficheMeta)) {
                    corpusExtractor.add(ficheMeta, null);
                }
            }
        } else {
            Predicate<FicheMeta> fichePredicate = extractParameters.getFichePredicate();
            for (FicheMeta ficheMeta : corpus.getFicheMetaList()) {
                if ((fichePredicate == null) || (fichePredicate.test(ficheMeta))) {
                    corpusExtractor.add(ficheMeta, null);
                }
            }
        }
        corpusExtractXMLPart.addCorpusExtract(corpusExtractor.getCorpusExtractResult());
    }

    private void addMotcleDynamic(ThesaurusExtractDef dynamicThesaurusExtractDef, Motcle motcle) throws IOException {
        if (dynamicThesaurusExtractDef == null) {
            return;
        }
        ThesaurusExtractor thesaurusExtractor = extractParameters.getExtractionContext().getExtractorProvider().getThesaurusExtractor(dynamicThesaurusExtractDef);
        thesaurusExtractor.add(motcle, null);
        thesaurusExtractXMLPart.addThesaurusExtract(thesaurusExtractor.getThesaurusExtractResult());
    }

    private void addThesaurusDynamic(ThesaurusExtractDef dynamicThesaurusExtractDef, Thesaurus thesaurus) throws IOException {
        if (dynamicThesaurusExtractDef == null) {
            return;
        }
        ExtractionContext extractionContext = extractParameters.getExtractionContext();
        ThesaurusExtractor thesaurusExtractor = extractionContext.getExtractorProvider().getThesaurusExtractor(dynamicThesaurusExtractDef);
        List<MotcleCondition.Entry> entryList = dynamicThesaurusExtractDef.getConditionEntryList();
        if (!entryList.isEmpty()) {
            SubsetKey thesaurusKey = thesaurus.getSubsetKey();
            boolean notEmpty = false;
            for (MotcleCondition.Entry entry : entryList) {
                if (entry.getMotcleQuery().getThesaurusCondition().accept(thesaurusKey)) {
                    notEmpty = true;
                    break;
                }
            }
            if (!notEmpty) {
                return;
            }
            SelectionContext selectionContext = SelectionContextBuilder.build(extractionContext)
                    .setSubsetAccessPredicate(extractionContext.getSubsetAccessPredicate())
                    .toSelectionContext();
            MotcleSelector motcleSelector = MotcleSelectorBuilder.init(selectionContext).addAll(entryList).toMotcleSelector();
            for (Motcle motcle : thesaurus.getMotcleList()) {
                if (motcleSelector.test(motcle)) {
                    thesaurusExtractor.add(motcle, null);
                }
            }
        } else {
            for (Motcle motcle : thesaurus.getFirstLevelList()) {
                thesaurusExtractor.add(motcle, null);
            }
        }
        thesaurusExtractXMLPart.addThesaurusExtract(thesaurusExtractor.getThesaurusExtractResult());
    }

    private void addStatic(String staticTagName, ExtractionSource extractionSource) throws IOException {
        List<CorpusExtractResult> corpusExtractResultList = extractionSource.getStaticCorpusExtractResultList();
        List<ThesaurusExtractResult> thesaurusExtractResultList = extractionSource.getStaticThesaurusExtractResultList();
        if ((corpusExtractResultList.isEmpty()) && (thesaurusExtractResultList.isEmpty())) {
            return;
        }
        if (staticTagName != null) {
            openTag(staticTagName);
        }
        for (CorpusExtractResult corpusExtractResult : corpusExtractResultList) {
            corpusExtractXMLPart.addCorpusExtract(corpusExtractResult);
        }
        for (ThesaurusExtractResult thesaurusExtractResult : thesaurusExtractResultList) {
            thesaurusExtractXMLPart.addThesaurusExtract(thesaurusExtractResult);
        }
        if (staticTagName != null) {
            closeTag(staticTagName);
        }
    }

    private String getTagName(TagNameInfo tagNameInfo) {
        switch (tagNameInfo.getType()) {
            case TagNameInfo.CUSTOM_TYPE:
                return tagNameInfo.getCustomTagName();
            case TagNameInfo.NULL_TYPE:
                if (allowsNoEnclosingTag) {
                    return null;
                } else {
                    return "NULL";
                }
            default:
                return "extraction";
        }
    }

    private String getStaticTagName(TagNameInfo tagNameInfo) {
        switch (tagNameInfo.getType()) {
            case TagNameInfo.CUSTOM_TYPE:
                return tagNameInfo.getCustomTagName();
            case TagNameInfo.NULL_TYPE:
                return null;
            default:
                return "static";
        }
    }

}
