/* BdfServer_Commands - Copyright (c) 2012-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.commands.thesaurus;

import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.api.BdfServerEditor;
import fr.exemole.bdfserver.api.EditSession;
import fr.exemole.bdfserver.api.interaction.Domains;
import fr.exemole.bdfserver.tools.instruction.AbstractBdfCommand;
import fr.exemole.bdfserver.tools.instruction.BdfErrors;
import fr.exemole.bdfserver.tools.ui.UiUtils;
import java.text.ParseException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import net.fichotheque.ExistingIdException;
import net.fichotheque.ExistingSubsetException;
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.croisement.CroisementEditor;
import net.fichotheque.thesaurus.Motcle;
import net.fichotheque.thesaurus.ParentRecursivityException;
import net.fichotheque.thesaurus.Thesaurus;
import net.fichotheque.thesaurus.ThesaurusEditor;
import net.fichotheque.thesaurus.metadata.ThesaurusMetadata;
import net.fichotheque.thesaurus.metadata.ThesaurusMetadataEditor;
import net.fichotheque.tools.FichothequeTools;
import net.fichotheque.tools.corpus.CorpusTools;
import net.fichotheque.tools.duplication.DuplicationUtils;
import net.fichotheque.tools.duplication.SubsetMatch;
import net.fichotheque.tools.thesaurus.ThesaurusTools;
import net.fichotheque.utils.EligibilityUtils;
import net.mapeadores.util.exceptions.ShouldNotOccurException;
import net.mapeadores.util.localisation.Langs;
import net.mapeadores.util.logging.ErrorMessageException;
import net.mapeadores.util.request.RequestMap;


/**
 *
 * @author Vincent Calame
 */
public class ThesaurusCloneCommand extends AbstractBdfCommand {

    public final static String COMMANDNAME = "ThesaurusClone";
    public final static String COMMANDKEY = "_ THS-15";
    public static String NEWTHESAURUS_PARAMNAME = "newthesaurus";
    public static String REINITID_PARAMNAME = "reinitid";
    public static String COPYCROISEMENTS_PARAMNAME = "copycroisements";
    public static String CLONESATELLITE_PARAMNAME = "clonesatellite";
    public static String SATELLITECORPUS_PARAMNAME = "satellitecorpus";
    public static String NEWCORPUS_PREFIX = "newcorpus|";
    public static String CLONEFICHES_PREFIX = "clonefiches|";
    private Thesaurus thesaurus;
    private SubsetKey newThesaurusKey;
    private boolean reinitId;
    private boolean copyCroisements;
    private boolean cloneSatellite;
    private final Map<SubsetKey, CorpusClone> corpusCloneMap = new TreeMap<SubsetKey, CorpusClone>();

    public ThesaurusCloneCommand(BdfServer bdfServer, RequestMap requestMap) {
        super(bdfServer, requestMap);
    }

    @Override
    public boolean needSynchronisation() {
        return true;
    }

    @Override
    protected void doCommand() throws ErrorMessageException {
        ThesaurusEditor newThesaurusEditor = null;
        try (EditSession session = startEditSession(Domains.THESAURUS, COMMANDNAME)) {
            FichothequeEditor fichothequeEditor = session.getFichothequeEditor();
            ThesaurusMetadata thesaurusMetadata = thesaurus.getThesaurusMetadata();
            try {
                newThesaurusEditor = fichothequeEditor.createThesaurus(newThesaurusKey, thesaurusMetadata.getThesaurusType());
            } catch (ExistingSubsetException ese) {
                throw new ShouldNotOccurException("test before ExistingSubsetException");
            }
            ThesaurusMetadataEditor newThesaurusMetadataEditor = newThesaurusEditor.getThesaurusMetadataEditor();
            Langs langs = thesaurusMetadata.getAuthorizedLangs();
            if (langs != null) {
                newThesaurusMetadataEditor.setAuthorizedLangs(langs);
            }
            FichothequeTools.copy(thesaurusMetadata, newThesaurusMetadataEditor, " (2)");
            SubsetMatch subsetMatch = new SubsetMatch(thesaurus, newThesaurusEditor.getThesaurus());
            for (Motcle motcle : thesaurus.getFirstLevelList()) {
                cloneMotcle(subsetMatch, fichothequeEditor, newThesaurusEditor, motcle, null);
            }
            if (cloneSatellite) {
                cloneSatelliteCorpus(subsetMatch, fichothequeEditor, session.getBdfServerEditor());
            }
            if (copyCroisements) {
                CroisementEditor croisementEditor = fichothequeEditor.getCroisementEditor();
                DuplicationUtils.copyCroisements(croisementEditor, subsetMatch, EligibilityUtils.exclude(thesaurus));
                DuplicationUtils.replicateLinks(croisementEditor, subsetMatch);
            }
        }
        putResultObject(THESAURUS_OBJ, newThesaurusEditor.getThesaurus());
        setDone("_ done.thesaurus.thesaurusclone", thesaurus.getSubsetKeyString(), newThesaurusKey.getKeyString());
    }

    @Override
    protected void checkParameters() throws ErrorMessageException {
        checkFichothequeAdmin();
        thesaurus = requestHandler.getMandatoryThesaurus();
        reinitId = requestHandler.isTrue(REINITID_PARAMNAME);
        copyCroisements = requestHandler.isTrue(COPYCROISEMENTS_PARAMNAME);
        cloneSatellite = requestHandler.isTrue(CLONESATELLITE_PARAMNAME);
        String newThesaurusName = getMandatory(NEWTHESAURUS_PARAMNAME).trim();
        if (newThesaurusName.isEmpty()) {
            throw BdfErrors.error("_ error.empty.thesaurusname", newThesaurusName);
        }
        try {
            newThesaurusKey = SubsetKey.parse(SubsetKey.CATEGORY_THESAURUS, newThesaurusName);
            if (fichotheque.containsSubset(newThesaurusKey)) {
                throw BdfErrors.error("_ error.existing.thesaurus", newThesaurusKey.getKeyString());
            }
        } catch (ParseException pe) {
            throw BdfErrors.error("_ error.wrong.thesaurusname", newThesaurusName);
        }
        if (cloneSatellite) {
            initSatelliteCorpus(requestHandler.getTokens(SATELLITECORPUS_PARAMNAME));
        }
    }

    private void initSatelliteCorpus(String[] values) throws ErrorMessageException {
        if (values.length == 0) {
            return;
        }
        Set<String> set = new HashSet<String>();
        for (String value : values) {
            set.add(value);
        }
        for (Corpus corpus : thesaurus.getSatelliteCorpusList()) {
            String corpusName = corpus.getSubsetName();
            if (set.contains(corpusName)) {
                String newCorpusName = getMandatory(NEWCORPUS_PREFIX + corpusName).trim();
                if (newCorpusName.length() == 0) {
                    throw BdfErrors.error("_ error.empty.corpusname", newCorpusName);
                }
                SubsetKey newCorpusKey;
                try {
                    newCorpusKey = SubsetKey.parse(SubsetKey.CATEGORY_CORPUS, newCorpusName);
                    if (fichotheque.containsSubset(newCorpusKey)) {
                        throw BdfErrors.error("_ error.existing.corpus", newCorpusKey.getKeyString());
                    }
                } catch (ParseException pe) {
                    throw BdfErrors.error("_ error.wrong.corpusname", newCorpusName);
                }
                if (corpusCloneMap.containsKey(newCorpusKey)) {
                    throw BdfErrors.error("_ error.existing.corpus", newCorpusKey.getKeyString());
                } else {
                    boolean cloneFiches = requestHandler.isTrue(CLONEFICHES_PREFIX + corpusName);
                    corpusCloneMap.put(newCorpusKey, new CorpusClone(corpus, newCorpusKey, cloneFiches));
                }
            }
        }
    }


    private void cloneMotcle(SubsetMatch subsetMatch, FichothequeEditor fichothequeEditor, ThesaurusEditor newThesaurusEditor, Motcle motcle, Motcle parentMotcle) {
        int id;
        if (reinitId) {
            id = -1;
        } else {
            id = motcle.getId();
        }
        Motcle newMotcle = null;
        try {
            newMotcle = newThesaurusEditor.createMotcle(id, motcle.getIdalpha());
        } catch (ExistingIdException | ParseException e) {
            throw new ShouldNotOccurException("clone motcle");
        }
        if (parentMotcle != null) {
            try {
                newThesaurusEditor.setParent(newMotcle, parentMotcle);
            } catch (ParentRecursivityException pre) {
                throw new ShouldNotOccurException("clone motcle");
            }
        }
        ThesaurusTools.copy(newThesaurusEditor, motcle, newMotcle);
        subsetMatch.add(motcle, newMotcle);
        for (Motcle child : motcle.getChildList()) {
            cloneMotcle(subsetMatch, fichothequeEditor, newThesaurusEditor, child, newMotcle);
        }
    }

    private void cloneSatelliteCorpus(SubsetMatch masterSubsetMatch, FichothequeEditor fichothequeEditor, BdfServerEditor bdfServerEditor) {
        if (corpusCloneMap.isEmpty()) {
            return;
        }
        Subset masterSubset = masterSubsetMatch.getDestinationSubset();
        CroisementEditor croisementEditor = fichothequeEditor.getCroisementEditor();
        for (CorpusClone corpusClone : corpusCloneMap.values()) {
            Corpus origin = corpusClone.origin;
            CorpusEditor destinationEditor;
            try {
                destinationEditor = fichothequeEditor.createCorpus(corpusClone.destinationCorpusKey, masterSubset);
            } catch (ExistingSubsetException ese) {
                throw new ShouldNotOccurException("test before ExistingSubsetException");
            }
            CorpusTools.copy(origin.getCorpusMetadata(), destinationEditor.getCorpusMetadataEditor());
            UiUtils.copyUi(bdfServerEditor, origin, destinationEditor.getCorpus());
            if (corpusClone.cloneFiches) {
                SubsetMatch ficheMatch = new SubsetMatch(origin, destinationEditor.getCorpus());
                for (FicheMeta originFiche : origin.getFicheMetaList()) {
                    FicheMeta destinationFiche = null;
                    SubsetMatch.Entry masterEntry = masterSubsetMatch.getEntryByOrigin(originFiche.getId());
                    try {
                        destinationFiche = destinationEditor.createFiche(masterEntry.getDestination().getId());
                    } catch (ExistingIdException eie) {
                        throw new ShouldNotOccurException("ExistingId test done before");
                    } catch (NoMasterIdException eie) {
                        throw new ShouldNotOccurException("NoMasterId test done before");
                    }
                    CorpusTools.copy(destinationEditor, originFiche, destinationFiche, originFiche.getFicheAPI(true));
                    ficheMatch.add(originFiche, destinationFiche);
                }
                if (copyCroisements) {
                    DuplicationUtils.copyCroisements(croisementEditor, ficheMatch, EligibilityUtils.exclude(origin));
                    DuplicationUtils.replicateLinks(croisementEditor, ficheMatch);
                }
            }
        }
    }


    private static class CorpusClone {

        private final Corpus origin;
        private final SubsetKey destinationCorpusKey;
        private final boolean cloneFiches;

        private CorpusClone(Corpus origin, SubsetKey destinationCorpusKey, boolean cloneFiches) {
            this.origin = origin;
            this.destinationCorpusKey = destinationCorpusKey;
            this.cloneFiches = cloneFiches;
        }

    }

}
