/* BdfServer - Copyright (c) 2010-2024 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.balayage.engine.runners;

import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.tools.balayage.BdfBalayageUtils;
import fr.exemole.bdfserver.tools.balayage.engine.BalayageLog;
import fr.exemole.bdfserver.tools.balayage.engine.BalayageParameters;
import fr.exemole.bdfserver.tools.balayage.engine.EngineUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.FicheMeta;
import net.fichotheque.corpus.Fiches;
import net.fichotheque.corpus.SortConstants;
import net.fichotheque.exportation.balayage.BalayageConstants;
import net.fichotheque.exportation.balayage.BalayageDef;
import net.fichotheque.exportation.balayage.BalayageDescription;
import net.fichotheque.exportation.balayage.BalayageMode;
import net.fichotheque.exportation.balayage.BalayageOutput;
import net.fichotheque.exportation.balayage.BalayageUnit;
import net.fichotheque.extraction.ExtractionSource;
import net.fichotheque.extraction.def.ExtractionDef;
import net.fichotheque.extraction.run.CorpusExtractResult;
import net.fichotheque.extraction.run.ThesaurusExtractResult;
import net.fichotheque.selection.FicheQuery;
import net.fichotheque.selection.FicheSelector;
import net.fichotheque.selection.MotcleSelector;
import net.fichotheque.selection.SelectionContext;
import net.fichotheque.thesaurus.Motcle;
import net.fichotheque.thesaurus.Thesaurus;
import net.fichotheque.tools.corpus.FichesBuilder;
import net.fichotheque.tools.extraction.ExtractionEngine;
import net.fichotheque.tools.extraction.ExtractionEngineUtils;
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.date.FuzzyDate;
import net.mapeadores.util.text.ValueResolver;
import org.xml.sax.SAXException;


/**
 *
 * @author Vincent Calame
 */
public class TransformBalayageUnitRunner extends AbstractBalayageUnitRunner {

    private final BalayageDescription balayageDescription;
    private final BalayageDef balayageDef;
    private TransformOutputRunner[] outputRunnerArray;
    private ExtractionDef extractionDef;
    private boolean testCurrentDate = false;
    private final InternalExtractionSource internalExtractionSource;

    public TransformBalayageUnitRunner(BalayageParameters balayageParameters, int balayageIndex, BalayageUnit balayageUnit) {
        super(balayageParameters, balayageIndex, balayageUnit);
        this.balayageDescription = balayageParameters.getBalayageDescription();
        BdfServer bdfServer = balayageParameters.getBdfServer();
        this.balayageDef = balayageParameters.getBalayageDef();
        List<TransformOutputRunner> outputList = new ArrayList<TransformOutputRunner>();
        int p = 0;
        for (BalayageOutput output : balayageUnit.getOutputList()) {
            TransformOutputRunner outputRunner = new TransformOutputRunner(balayageParameters, p, output, balayageIndex);
            if (outputRunner.isValid()) {
                outputList.add(outputRunner);
                p++;
            }
        }
        outputRunnerArray = outputList.toArray(new TransformOutputRunner[outputList.size()]);
        String type = balayageUnit.getType();
        String extractionPath = balayageUnit.getExtractionPath();
        if (!extractionPath.isEmpty()) {
            try {
                extractionDef = BdfBalayageUtils.getExtractionDef(bdfServer, balayageDescription, extractionPath);
                if (extractionDef == null) {
                    balayageLog.addBalayageUnitError(balayageIndex, balayageUnit, BalayageLog.EXTRACTION_NOT_FOUND, extractionPath, null);
                }
            } catch (SAXException ube) {
                balayageLog.addBalayageUnitError(balayageIndex, balayageUnit, BalayageLog.EXTRACTION_EXCEPTION, extractionPath, ube.getMessage());
            }
        }
        if (extractionDef == null) {
            extractionDef = BdfBalayageUtils.getDefaultExtractionDef(balayageUnit.getType());
        }
        List<CorpusExtractResult> corpusExtractResultList = ExtractionEngineUtils.runCorpusExtractDef(extractionDef.getStaticCorpusExtractDefList(), extractionContext, balayageParameters.getSelectedFiches());
        List<ThesaurusExtractResult> thesaurusExtractResultList = ExtractionEngineUtils.runThesaurusExtractDef(extractionDef.getStaticThesaurusExtractDefList(), extractionContext);
        internalExtractionSource = new InternalExtractionSource(corpusExtractResultList, thesaurusExtractResultList);
        if (type.equals(BalayageConstants.FICHE_TYPE)) {
            BalayageMode balayageMode = balayageParameters.getBalayageMode();
            if (balayageMode != null) {
                if (balayageMode.isWithCurrentDateLimit()) {
                    testCurrentDate = true;
                }
            }
        }
    }

    @Override
    protected boolean isEmpty() {
        return (outputRunnerArray.length == 0);
    }

    @Override
    protected void run(String balayageUnitType) {
        switch (balayageUnitType) {
            case BalayageConstants.CORPUS_TYPE:
                runCorpusBalayage();
                break;
            case BalayageConstants.FICHE_TYPE:
                runFicheBalayage();
                break;
            case BalayageConstants.MOTCLE_TYPE:
                runMotcleBalayage();
                break;
            case BalayageConstants.THESAURUS_TYPE:
                runThesaurusBalayage();
                break;
            case BalayageConstants.UNIQUE_TYPE:
                runUniqueBalayage();
                break;
        }
    }

    @Override
    public void dispose() {
        super.dispose();
        for (int i = 0; i < outputRunnerArray.length; i++) {
            outputRunnerArray[i].dispose();
            outputRunnerArray[i] = null;
        }
        outputRunnerArray = null;
        extractionDef = null;
    }

    private void runCorpusBalayage() {
        Corpus[] corpusArray = SelectionUtils.toCorpusArray(fichotheque, balayageUnit.getFicheQuery(), extractionContext.getSubsetAccessPredicate());
        for (Corpus corpus : corpusArray) {
            internalExtractionSource.setDynamicObject(corpus);
            String extractionString = getExtractionString();
            ValueResolver patternValueSet = EngineUtils.getValueResolver(corpus);
            for (TransformOutputRunner outputRunner : outputRunnerArray) {
                outputRunner.write(corpus, patternValueSet, extractionString);
            }
        }
    }

    private void runFicheBalayage() {
        FicheQuery ficheQuery = balayageUnit.getFicheQuery();
        Predicate<FicheMeta> fichePredicate = (balayageUnit.isGlobalSelect()) ? balayageParameters.getSelectedFiches() : null;
        SelectionContext selectionContext = SelectionContextBuilder.build(extractionContext, defaultWorkingLang)
                .setSubsetAccessPredicate(extractionContext.getSubsetAccessPredicate())
                .setFichePredicate(fichePredicate)
                .toSelectionContext();
        FicheSelector ficheSelector = FicheSelectorBuilder.init(selectionContext).add(ficheQuery).toFicheSelector();
        Fiches fiches = FichesBuilder.build(SortConstants.ID_ASC)
                .populate(ficheSelector)
                .toFiches();
        for (Fiches.Entry entry : fiches.getEntryList()) {
            FuzzyDate currentDate = FuzzyDate.current();
            for (FicheMeta ficheMeta : entry.getFicheMetaList()) {
                if (testCurrentDate) {
                    if (FuzzyDate.compare(ficheMeta.getModificationDate(), currentDate) != 0) {
                        continue;
                    }
                }
                internalExtractionSource.setDynamicObject(ficheMeta);
                String extractionString = getExtractionString();
                ValueResolver patternValueSet = EngineUtils.getValueResolver(ficheMeta);
                for (TransformOutputRunner outputRunner : outputRunnerArray) {
                    outputRunner.write(ficheMeta, patternValueSet, extractionString);
                }
            }
        }
    }

    private void runUniqueBalayage() {
        internalExtractionSource.setDynamicObject(null);
        String extractionString = getExtractionString();
        ValueResolver patternValueSet = EngineUtils.getValueResolver(null);
        for (TransformOutputRunner outputRunner : outputRunnerArray) {
            outputRunner.write(null, patternValueSet, extractionString);
        }
    }

    private void runThesaurusBalayage() {
        Thesaurus[] thesaurusArray = SelectionUtils.toThesaurusArray(fichotheque, balayageUnit.getMotcleQuery(), extractionContext.getSubsetAccessPredicate());
        for (Thesaurus thesaurus : thesaurusArray) {
            internalExtractionSource.setDynamicObject(thesaurus);
            String extractionString = getExtractionString();
            ValueResolver patternValueSet = EngineUtils.getValueResolver(thesaurus);
            for (TransformOutputRunner outputRunner : outputRunnerArray) {
                outputRunner.write(thesaurus, patternValueSet, extractionString);
            }
        }
    }

    private void runMotcleBalayage() {
        SelectionContext selectionContext = SelectionContextBuilder.build(extractionContext, defaultWorkingLang)
                .setSubsetAccessPredicate(extractionContext.getSubsetAccessPredicate())
                .toSelectionContext();
        MotcleSelector motcleSelector = MotcleSelectorBuilder.init(selectionContext).add(balayageUnit.getMotcleQuery(), null).toMotcleSelector();
        for (Thesaurus thesaurus : motcleSelector.getThesaurusList()) {
            for (Motcle motcle : thesaurus.getMotcleList()) {
                if (motcleSelector.test(motcle)) {
                    writeMotcle(motcle);
                }
            }
        }
    }

    private void writeMotcle(Motcle motcle) {
        internalExtractionSource.setDynamicObject(motcle);
        String extractionString = getExtractionString();
        ValueResolver patternValueSet = EngineUtils.getValueResolver(motcle);
        for (TransformOutputRunner outputRunner : outputRunnerArray) {
            outputRunner.write(motcle, patternValueSet, extractionString);
        }
    }

    private String getExtractionString() {
        return ExtractionEngine.init(extractParameters, extractionDef).prettyXml(balayageDef.ignoreTransformation()).run(internalExtractionSource);
    }


    private static class InternalExtractionSource implements ExtractionSource {

        private final List<CorpusExtractResult> corpusExtractResultList;
        private final List<ThesaurusExtractResult> thesaurusExtractResultList;
        private Object dynamicObject;

        private InternalExtractionSource(List<CorpusExtractResult> corpusExtractorList, List<ThesaurusExtractResult> thesaurusExtractResultList) {
            this.corpusExtractResultList = corpusExtractorList;
            this.thesaurusExtractResultList = thesaurusExtractResultList;
        }

        @Override
        public Object getDynamicObject() {
            return dynamicObject;
        }

        private void setDynamicObject(Object dynamicObject) {
            this.dynamicObject = dynamicObject;
        }

        @Override
        public List<CorpusExtractResult> getStaticCorpusExtractResultList() {
            return corpusExtractResultList;
        }

        @Override
        public List<ThesaurusExtractResult> getStaticThesaurusExtractResultList() {
            return thesaurusExtractResultList;
        }

    }

}
