/* FichothequeLib_Tools - Copyright (c) 2024-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.parsers.junction;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.fichotheque.SubsetItem;
import net.fichotheque.addenda.Addenda;
import net.fichotheque.junction.Liaison;
import net.fichotheque.tools.junction.JunctionChangeBuilder;
import net.fichotheque.tools.junction.JunctionChangesBuilder;
import net.fichotheque.utils.JunctionUtils;
import net.fichotheque.junction.Tie;
import net.fichotheque.junction.JunctionEditor;
import net.fichotheque.junction.JunctionChanges;


/**
 *
 * @author Vincent Calame
 */
class DocumentOrderManager {

    private final SubsetItem mainSubsetItem;
    private final Addenda addenda;
    private final String mode;
    private final int weight;
    private final List<Object> orderList = new ArrayList<Object>();

    DocumentOrderManager(SubsetItem mainSubsetItem, Addenda addenda, String mode, int weight) {
        this.mainSubsetItem = mainSubsetItem;
        this.addenda = addenda;
        this.mode = mode;
        this.weight = weight;
    }

    void add(Object orderObject) {
        orderList.add(orderObject);
    }

    void replace(String basename, int newDocumentId) {
        int idx = orderList.indexOf(basename);
        orderList.set(idx, newDocumentId);
    }

    void save(JunctionEditor junctionEditor) {
        EditEngine editEngine = new EditEngine();
        editEngine.initLiaisonInfos();
        editEngine.scanNewOrder(orderList);
        JunctionChanges junctionChanges = editEngine.toJunctionChanges();
        junctionEditor.updateJunctions(mainSubsetItem, junctionChanges);
    }


    private class EditEngine {

        private final List<LiaisonInfo> infoList = new ArrayList<LiaisonInfo>();
        private final Map<Integer, LiaisonInfo> infoByIdMap = new HashMap<Integer, LiaisonInfo>();

        private EditEngine() {

        }

        private void initLiaisonInfos() {
            Collection<Liaison> liaisons = JunctionUtils.filter(mainSubsetItem.getJunctions(addenda), mode, weight);
            int currentIndex = 0;
            for (Liaison liaison : liaisons) {
                LiaisonInfo liaisonInfo = new LiaisonInfo(liaison, currentIndex);
                infoList.add(liaisonInfo);
                infoByIdMap.put(liaison.getSubsetItem().getId(), liaisonInfo);
            }
        }

        private void scanNewOrder(List<Object> orderList) {
            int newIndex = 0;
            for (Object obj : orderList) {
                if (obj instanceof Integer) {
                    LiaisonInfo liaisonInfo = infoByIdMap.get((Integer) obj);
                    if (liaisonInfo != null) {
                        initPosition(liaisonInfo, newIndex);
                        newIndex++;
                    }
                }
            }
            for (LiaisonInfo liaisonInfo : infoList) {
                if (liaisonInfo.newIndex == -1) {
                    initPosition(liaisonInfo, newIndex);
                    newIndex++;
                }
            }
        }

        private void initPosition(LiaisonInfo liaisonInfo, int newIndex) {
            liaisonInfo.newIndex = newIndex;
            liaisonInfo.newPosition = infoList.get(newIndex).currentPosition;
        }

        private JunctionChanges toJunctionChanges() {
            JunctionChangesBuilder builder = new JunctionChangesBuilder();
            for (LiaisonInfo liaisonInfo : infoList) {
                JunctionChangeBuilder changeBuilder = new JunctionChangeBuilder();
                changeBuilder.addTie(liaisonInfo.getNewTie());
                builder.addEntry(liaisonInfo.liaison.getSubsetItem(), changeBuilder.toJunctionChange());
            }
            return builder.toJunctionChanges();
        }


    }


    private static class LiaisonInfo {

        private final Liaison liaison;
        private final int currentPosition;
        private final int currentIndex;
        private int newIndex = -1;
        private int newPosition = -1;

        private LiaisonInfo(Liaison liaison, int currentIndex) {
            this.liaison = liaison;
            this.currentPosition = liaison.getPosition();
            this.currentIndex = currentIndex;
        }

        private Tie getNewTie() {
            Tie tie = liaison.getTie();
            int position1;
            int position2;
            if (liaison.getPositionNumber() == 1) {
                position1 = newPosition;
                position2 = tie.getPosition2();
            } else {
                position1 = tie.getPosition1();
                position2 = newPosition;
            }
            return JunctionUtils.toTie(tie.getMode(), tie.getWeight(), position1, position2);
        }

    }

}
