/* FichothequeLib_Tools - Copyright (c) 2012-2017 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.croisement.CroisementChange;
import net.fichotheque.croisement.CroisementChanges;
import net.fichotheque.croisement.CroisementEditor;
import net.fichotheque.croisement.Liaison;
import net.fichotheque.croisement.Lien;
import net.fichotheque.tools.croisement.CroisementChangeEngine;
import net.fichotheque.utils.CroisementUtils;


/**
 *
 * @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 croisementSubsetKey = reponderationParameters.getCroisementSubsetKey();
        Subset croisementSubset = fichotheque.getSubset(croisementSubsetKey);
        if (croisementSubset == null) {
            addError(ReponderationLog.CROISEMENT_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();
        CroisementEditor croisementEditor = fichothequeEditor.getCroisementEditor();
        for (SubsetItem originSubsetItem : originSubsetItemList) {
            CroisementChangeEngine croisementChangeEngine = CroisementChangeEngine.appendOrWeightReplaceEngine(originSubsetItem);
            Collection<Liaison> liaisons = CroisementUtils.filter(fichotheque.getCroisements(originSubsetItem, croisementSubset), oldMode);
            boolean done = false;
            for (Liaison liaison : liaisons) {
                int weight = liaison.getLien().getWeight();
                if ((oldWeight == -1) || (weight == oldWeight)) {
                    if (newWeight != -1) {
                        weight = newWeight;
                    }
                    SubsetItem subsetItem = liaison.getSubsetItem();
                    croisementChangeEngine.addLien(subsetItem, newMode, weight);
                    addResult(originSubsetItem.getId(), subsetItem.getId());
                    done = true;
                }
            }
            if (done) {
                CroisementChanges croisementChanges = croisementChangeEngine.toCroisementChanges();
                if (!oldMode.equals(newMode)) {
                    croisementChanges = alter(croisementChanges, oldMode);
                }
                croisementEditor.updateCroisements(originSubsetItem, croisementChanges);
            }
        }
    }

    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 croisementId) {
        reponderationLog.addResult(originId, croisementId);
    }

    private static CroisementChanges alter(CroisementChanges croisementChanges, String oldMode) {
        List<CroisementChanges.Entry> alteredList = new ArrayList<CroisementChanges.Entry>();
        for (CroisementChanges.Entry entry : croisementChanges.getEntryList()) {
            alteredList.add(CroisementUtils.toEntry(entry.getSubsetItem(), new WrappedCroisementChange(oldMode, entry.getCroisementChange())));
        }
        return new WrappedCroisementChanges(croisementChanges.getRemovedList(), Collections.unmodifiableList(alteredList));
    }


    private static class WrappedCroisementChanges implements CroisementChanges {

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

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

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

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

    }


    private static class WrappedCroisementChange implements CroisementChange {

        private final List<String> oldModeList;
        private final CroisementChange croisementChange;

        private WrappedCroisementChange(String oldMode, CroisementChange croisementChange) {
            this.oldModeList = Collections.singletonList(oldMode);
            this.croisementChange = croisementChange;

        }

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

        @Override
        public List<Lien> getChangedLienList() {
            return croisementChange.getChangedLienList();
        }

    }

}
