/* FichothequeLib_Tools - 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 net.fichotheque.tools.reponderation;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import net.fichotheque.Fichotheque;
import net.fichotheque.FichothequeEditor;
import net.fichotheque.Subset;
import net.fichotheque.SubsetItem;
import net.fichotheque.SubsetKey;
import net.fichotheque.junction.JunctionChange;
import net.fichotheque.junction.JunctionChanges;
import net.fichotheque.junction.JunctionEditor;
import net.fichotheque.junction.Liaison;
import net.fichotheque.junction.Tie;
import net.fichotheque.tools.junction.JunctionChangeEngine;
import net.fichotheque.utils.JunctionUtils;


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

    private final ReponderationLog reponderationLog;
    private final Fichotheque fichotheque;
    private final ReponderationParameters reponderationParameters;
    private final FichothequeEditor fichothequeEditor;

    private ReponderationEngine(ReponderationParameters reponderationParameters, FichothequeEditor fichothequeEditor) {
        this.reponderationLog = new ReponderationLog(reponderationParameters);
        this.fichothequeEditor = fichothequeEditor;
        this.fichotheque = fichothequeEditor.getFichotheque();
        this.reponderationParameters = reponderationParameters;
    }

    private ReponderationEngine(ReponderationParameters reponderationParameters, Fichotheque fichotheque) {
        this.reponderationLog = new ReponderationLog(reponderationParameters);
        this.fichotheque = fichotheque;
        this.reponderationParameters = reponderationParameters;
        this.fichothequeEditor = null;
    }

    public static ReponderationLog run(ReponderationParameters reponderationDef, FichothequeEditor fichothequeEditor) {
        ReponderationEngine reponderationEngine = new ReponderationEngine(reponderationDef, fichothequeEditor);
        reponderationEngine.innerRun(false);
        return reponderationEngine.reponderationLog;
    }

    public static ReponderationLog test(ReponderationParameters reponderationDef, Fichotheque fichotheque) {
        ReponderationEngine reponderationEngine = new ReponderationEngine(reponderationDef, fichotheque);
        reponderationEngine.innerRun(true);
        return reponderationEngine.reponderationLog;
    }

    private void innerRun(boolean test) {
        SubsetKey originSubsetKey = reponderationParameters.getOriginSubsetKey();
        Subset origineSubset = fichotheque.getSubset(originSubsetKey);
        if (origineSubset == null) {
            addError(ReponderationLog.ORIGIN_UNKNOWNSUBSET_ERROR);
        }
        SubsetKey junctionSubsetKey = reponderationParameters.getJunctionSubsetKey();
        Subset junctionSubset = fichotheque.getSubset(junctionSubsetKey);
        if (junctionSubset == null) {
            addError(ReponderationLog.JUNCTION_UNKNOWNSUBSET_ERROR);
        }
        if (hasError()) {
            return;
        }
        List<SubsetItem> originSubsetItemList = getOriginSubsetItemList(origineSubset);
        if (hasError()) {
            return;
        }
        if (test) {
            return;
        }
        String oldMode = reponderationParameters.getOldMode();
        String newMode = reponderationParameters.getNewMode();
        int oldWeight = reponderationParameters.getOldWeight();
        int newWeight = reponderationParameters.getNewWeight();
        JunctionEditor junctionEditor = fichothequeEditor.getJunctionEditor();
        for (SubsetItem originSubsetItem : originSubsetItemList) {
            JunctionChangeEngine junctionChangeEngine = JunctionChangeEngine.appendOrWeightReplaceEngine(originSubsetItem);
            Collection<Liaison> liaisons = JunctionUtils.filter(fichotheque.getJunctions(originSubsetItem, junctionSubset), oldMode);
            boolean done = false;
            for (Liaison liaison : liaisons) {
                int weight = liaison.getTie().getWeight();
                if ((oldWeight == -1) || (weight == oldWeight)) {
                    if (newWeight != -1) {
                        weight = newWeight;
                    }
                    SubsetItem subsetItem = liaison.getSubsetItem();
                    junctionChangeEngine.addTie(subsetItem, newMode, weight);
                    addResult(originSubsetItem.getId(), subsetItem.getId());
                    done = true;
                }
            }
            if (done) {
                JunctionChanges junctionChanges = junctionChangeEngine.toJunctionChanges();
                if (!oldMode.equals(newMode)) {
                    junctionChanges = alter(junctionChanges, oldMode);
                }
                junctionEditor.updateJunctions(originSubsetItem, junctionChanges);
            }
        }
    }

    private List<SubsetItem> getOriginSubsetItemList(Subset origineCorpus) {
        List<SubsetItem> originSubsetItemList = new ArrayList<SubsetItem>();
        int ficheCount = reponderationParameters.getOriginIdCount();
        for (int i = 0; i < ficheCount; i++) {
            int originSubsetItem = reponderationParameters.getOriginId(i);
            SubsetItem subsetItem = origineCorpus.getSubsetItemById(originSubsetItem);
            if (subsetItem == null) {
                addIdError(ReponderationLog.MISSING_ORIGIN_ID_ERROR, originSubsetItem);
            } else {
                originSubsetItemList.add(subsetItem);
            }
        }
        return originSubsetItemList;
    }

    private void addError(String errorKey) {
        reponderationLog.addErrorKey(errorKey);
    }

    private void addIdError(String errorKey, int id) {
        reponderationLog.addIdError(errorKey, id);
    }

    private boolean hasError() {
        return reponderationLog.hasError();
    }

    private void addResult(int originId, int junctionId) {
        reponderationLog.addResult(originId, junctionId);
    }

    private static JunctionChanges alter(JunctionChanges junctionChanges, String oldMode) {
        List<JunctionChanges.Entry> alteredList = new ArrayList<JunctionChanges.Entry>();
        for (JunctionChanges.Entry entry : junctionChanges.getEntryList()) {
            alteredList.add(JunctionUtils.toEntry(entry.getSubsetItem(), new WrappedJunctionChange(oldMode, entry.getJunctionChange())));
        }
        return new WrappedJunctionChanges(junctionChanges.getRemovedList(), Collections.unmodifiableList(alteredList));
    }


    private static class WrappedJunctionChanges implements JunctionChanges {

        private final List<SubsetItem> removedList;
        private final List<JunctionChanges.Entry> entryList;

        private WrappedJunctionChanges(List<SubsetItem> removedSubsetItemList, List<JunctionChanges.Entry> entryList) {
            this.removedList = removedSubsetItemList;
            this.entryList = entryList;
        }

        @Override
        public List<SubsetItem> getRemovedList() {
            return removedList;
        }

        @Override
        public List<JunctionChanges.Entry> getEntryList() {
            return entryList;
        }

    }


    private static class WrappedJunctionChange implements JunctionChange {

        private final List<String> oldModeList;
        private final JunctionChange junctionChange;

        private WrappedJunctionChange(String oldMode, JunctionChange junctionChange) {
            this.oldModeList = Collections.singletonList(oldMode);
            this.junctionChange = junctionChange;

        }

        @Override
        public List<String> getRemovedModeList() {
            return oldModeList;
        }

        @Override
        public List<Tie> getChangedTieList() {
            return junctionChange.getChangedTieList();
        }

    }

}
