/* BdfServer - Copyright (c) 2006-2022 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;

import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.api.EditSession;
import fr.exemole.bdfserver.api.instruction.BdfParameters;
import fr.exemole.bdfserver.api.interaction.InteractionConstants;
import fr.exemole.bdfserver.api.users.BdfUser;
import fr.exemole.bdfserver.tools.configuration.ConfigurationUtils;
import fr.exemole.bdfserver.tools.instruction.BdfCommandUtils;
import fr.exemole.bdfserver.tools.users.BdfUserUtils;
import java.io.File;
import java.text.ParseException;
import net.fichotheque.ExistingIdException;
import net.fichotheque.FichothequeEditor;
import net.fichotheque.Subset;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.CorpusEditor;
import net.fichotheque.corpus.FicheMeta;
import net.fichotheque.corpus.NoMasterIdException;
import net.fichotheque.corpus.fiche.Fiche;
import net.fichotheque.permission.PermissionSummary;
import net.fichotheque.tools.corpus.CorpusTools;
import net.fichotheque.tools.corpus.FieldGenerationEngine;
import net.fichotheque.tools.parsers.CroisementParseEngine;
import net.fichotheque.tools.parsers.FicheParseEngine;
import net.fichotheque.tools.parsers.FicheParser;
import net.fichotheque.tools.parsers.ParseContext;
import net.fichotheque.tools.parsers.croisement.DocumentTokenParser;
import net.fichotheque.tools.parsers.croisement.IllustrationTokenParser;
import net.fichotheque.tools.parsers.croisement.TokenKey;
import net.fichotheque.tools.parsers.croisement.TokenKeys;
import net.mapeadores.util.annotation.Nullable;
import net.mapeadores.util.exceptions.ImplementationException;
import net.mapeadores.util.exceptions.ShouldNotOccurException;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.primitives.FuzzyDate;
import net.mapeadores.util.request.FileValue;
import net.mapeadores.util.request.RequestMap;
import net.mapeadores.util.request.RequestUtils;


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

    private final BdfParameters bdfParameters;
    private final BdfServer bdfServer;
    private final PermissionSummary permissionSummary;
    private final FichothequeEditor fichothequeEditor;
    private final RequestMap globalRequestMap;
    private final short ficheParseType;
    private final FicheParser.Parameters ficheParserParameters;
    private final Lang workingLang;
    private final ParseContext parseContext;

    private EditionEngine(EditSession editSession, BdfParameters bdfParameters, RequestMap requestMap, short ficheParseType) {
        this.bdfParameters = bdfParameters;
        this.bdfServer = bdfParameters.getBdfServer();
        this.permissionSummary = bdfParameters.getPermissionSummary();
        this.fichothequeEditor = editSession.getFichothequeEditor();
        this.globalRequestMap = requestMap;
        this.ficheParseType = ficheParseType;
        BdfUser bdfUser = bdfParameters.getBdfUser();
        this.workingLang = bdfUser.getWorkingLang();
        this.ficheParserParameters = new FicheParser.Parameters(bdfUser, bdfServer.getL10nManager().getCodeCatalog(), bdfParameters.getHtmlCleaner(), BdfUserUtils.getTypoOptions(bdfUser));
        ficheParserParameters.setUserRedacteur(bdfUser.getRedacteur());
        this.parseContext = new ParseContext(fichothequeEditor, bdfServer.getPolicyManager().getPolicyProvider(), bdfServer.getThesaurusLangChecker(), bdfServer.getExternalSourceProvider(), permissionSummary.getSubsetAccessPredicate());
    }

    private void run(FicheMeta mainFicheMeta) {
        String prefixType = globalRequestMap.getParameter(InteractionConstants.PREFIXTYPE_PARAMNAME);
        if (prefixType == null) {
            runDefault(mainFicheMeta);
        } else {
            switch (prefixType) {
                case InteractionConstants.SATELLITES_PREFIXTYPE_PARAMVALUE:
                    runWithSatellites(mainFicheMeta);
                    break;
                default:
                    runDefault(mainFicheMeta);
                    break;
            }
        }
    }

    private void runDefault(FicheMeta ficheMeta) {
        newParser(ficheMeta, null)
                .run()
                .saveFiche();
    }

    private void runWithSatellites(FicheMeta mainFicheMeta) {
        SubsetKey mainCorpusKey = mainFicheMeta.getSubsetKey();
        Parser mainParser = newParser(mainFicheMeta, mainCorpusKey.getSubsetName())
                .run()
                .saveFiche();
        Fiche mainFiche = mainParser.getFiche();
        String[] satellitePrefixArray = globalRequestMap.getParameterValues(InteractionConstants.PREFIXLIST_PARAMNAME);
        if (satellitePrefixArray != null) {
            int id = mainFicheMeta.getId();
            for (String prefix : satellitePrefixArray) {
                Corpus satelliteCorpus = getSatelliteCorpus(mainCorpusKey, prefix);
                if (satelliteCorpus != null) {
                    FicheMeta satelliteFicheMeta = satelliteCorpus.getFicheMetaById(id);
                    if (satelliteFicheMeta != null) {
                        if (permissionSummary.canWrite(satelliteFicheMeta)) {
                            parseSatellite(satelliteFicheMeta, prefix, mainFiche);
                        }
                    } else if (permissionSummary.canCreate(satelliteCorpus)) {
                        satelliteFicheMeta = createSatelliteFiche(fichothequeEditor.getCorpusEditor(satelliteCorpus.getSubsetKey()), id);
                        parseSatellite(satelliteFicheMeta, prefix, mainFiche);
                    }
                }
            }
        }
    }

    private void parseSatellite(FicheMeta satelliteFicheMeta, String prefix, Fiche mainFiche) {
        Parser parser = newParser(satelliteFicheMeta, prefix)
                .run();
        Fiche satelliteFiche = parser.getFiche();
        if ((satelliteFiche != null) && (mainFiche != null)) {
            satelliteFiche.setOwners(mainFiche.getOwners());
        }
        parser.saveFiche();
    }

    private Corpus getSatelliteCorpus(SubsetKey mainCorpusKey, String prefix) {
        try {
            SubsetKey corpusKey = SubsetKey.parse(SubsetKey.CATEGORY_CORPUS, prefix);
            if (!permissionSummary.hasAccess(corpusKey)) {
                return null;
            }
            Corpus satelliteCorpus = (Corpus) bdfServer.getFichotheque().getSubset(corpusKey);
            if (satelliteCorpus == null) {
                return null;
            }
            Subset masterSubset = satelliteCorpus.getMasterSubset();
            if (masterSubset == null) {
                return null;
            }
            if (!masterSubset.getSubsetKey().equals(mainCorpusKey)) {
                return null;
            }
            return satelliteCorpus;
        } catch (ParseException pe) {
            return null;
        }
    }

    private Parser newParser(FicheMeta ficheMeta, @Nullable String paramSuffix) {
        RequestMap requestMap;
        if (paramSuffix != null) {
            requestMap = RequestUtils.reduceToSuffix(globalRequestMap, paramSuffix + "_");
        } else {
            requestMap = globalRequestMap;
        }
        return new Parser(ficheMeta, requestMap);
    }

    private FicheMeta createSatelliteFiche(CorpusEditor satelliteEditor, int id) {
        FicheMeta ficheMeta;
        try {
            ficheMeta = satelliteEditor.createFiche(id);
        } catch (ExistingIdException eii) {
            throw new ImplementationException(eii);
        } catch (NoMasterIdException nmie) {
            throw new ShouldNotOccurException(nmie);
        }
        satelliteEditor.setDate(ficheMeta, FuzzyDate.current(), false);
        return ficheMeta;
    }

    public final static void replace(EditSession editSession, BdfParameters bdfParameters, RequestMap requestMap, FicheMeta ficheMeta) {
        EditionEngine editionEngine = new EditionEngine(editSession, bdfParameters, requestMap, FicheParseEngine.REPLACE_TYPE);
        editionEngine.run(ficheMeta);
    }

    public final static void update(EditSession editSession, BdfParameters bdfParameters, RequestMap requestMap, FicheMeta ficheMeta) {
        EditionEngine editionEngine = new EditionEngine(editSession, bdfParameters, requestMap, FicheParseEngine.UPDATE_TYPE);
        editionEngine.run(ficheMeta);
    }

    public final static void croisementOnly(EditSession editSession, BdfParameters bdfParameters, RequestMap requestMap, FicheMeta ficheMeta) {
        EditionEngine editionEngine = new EditionEngine(editSession, bdfParameters, requestMap, FicheParseEngine.NONE_TYPE);
        editionEngine.run(ficheMeta);
    }


    private class Parser {

        private final FicheMeta ficheMeta;
        private final RequestMap requestMap;
        private Fiche fiche = null;


        private Parser(FicheMeta ficheMeta, RequestMap requestMap) {
            this.requestMap = requestMap;
            this.ficheMeta = ficheMeta;
        }

        private Fiche getFiche() {
            return fiche;
        }

        private Parser run() {
            parseCroisement();
            parseFiche();
            return this;
        }

        private void parseCroisement() {
            CroisementParseEngine.run(ficheMeta, requestMap, parseContext, workingLang);
            TokenKeys tokenKeys = TokenKeys.build(requestMap.getParameterNameSet());
            if (tokenKeys.withIllustration()) {
                File tmpDirectory = ConfigurationUtils.getTmpDirectory(bdfServer);
                IllustrationTokenParser illustrationTokenParser = new IllustrationTokenParser(ficheMeta, tmpDirectory);
                for (TokenKey tokenKey : tokenKeys.getTokenKeyList(TokenKeys.ILLUSTRATION_FILE)) {
                    FileValue[] fileValues = requestMap.getFileValues(tokenKey.getParamName());
                    if (fileValues != null) {
                        illustrationTokenParser.addFromMultipart(tokenKey, fileValues);
                    }
                }
                for (TokenKey tokenKey : tokenKeys.getTokenKeyList(TokenKeys.ILLUSTRATION_REF)) {
                    illustrationTokenParser.parse(tokenKey, requestMap.getParameterValues(tokenKey.getParamName()));
                }
                illustrationTokenParser.save(fichothequeEditor);
            }
            if (tokenKeys.withDocument()) {
                File tmpDirectory = ConfigurationUtils.getTmpDirectory(bdfServer);
                DocumentTokenParser documentTokenParser = new DocumentTokenParser(ficheMeta, tmpDirectory);
                for (TokenKey tokenKey : tokenKeys.getTokenKeyList(TokenKeys.DOCUMENT_FILE)) {
                    FileValue[] fileValues = requestMap.getFileValues(tokenKey.getParamName());
                    if (fileValues != null) {
                        documentTokenParser.addFromMultipart(tokenKey, fileValues);
                    }
                }
                for (TokenKey tokenKey : tokenKeys.getTokenKeyList(TokenKeys.DOCUMENT_REF)) {
                    documentTokenParser.parse(tokenKey, requestMap.getParameterValues(tokenKey.getParamName()));
                }
                documentTokenParser.save(fichothequeEditor);
            }
        }

        private void parseFiche() {
            if (ficheParseType != FicheParseEngine.NONE_TYPE) {
                fiche = FicheParseEngine.run(ficheParserParameters, ficheMeta, requestMap, ficheParseType);
            }
        }

        private Parser saveFiche() {
            if (fiche != null) {
                CorpusEditor corpusEditor = fichothequeEditor.getCorpusEditor(ficheMeta.getSubsetKey());
                FieldGenerationEngine engine = BdfCommandUtils.buildEngine(bdfParameters, ficheMeta.getCorpus());
                CorpusTools.saveFiche(corpusEditor, ficheMeta, fiche, engine, true);
            }
            return this;
        }

    }

}
