/* FichothequeLib_Tools - Copyright (c) 2012-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 net.fichotheque.tools.extraction;

import java.util.AbstractList;
import java.util.List;
import java.util.Map;
import java.util.RandomAccess;
import java.util.SortedMap;
import java.util.TreeMap;
import net.fichotheque.SubsetKey;
import net.fichotheque.addenda.Addenda;
import net.fichotheque.addenda.Document;
import net.fichotheque.croisement.Croisement;
import net.fichotheque.croisement.LiaisonSortKey;
import net.fichotheque.croisement.Lien;
import net.fichotheque.extraction.def.AddendaExtractDef;
import net.fichotheque.extraction.def.DocumentFilter;
import net.fichotheque.extraction.run.AddendaExtractResult;
import net.fichotheque.extraction.run.AddendaExtractor;
import net.fichotheque.extraction.run.DocumentExtractInfo;
import net.fichotheque.utils.CroisementUtils;


/**
 *
 * @author Vincent Calame
 */
class AddendaExtractorImpl implements AddendaExtractor {

    private final Map<SubsetKey, ResultBuilder> builderMap = new TreeMap<SubsetKey, ResultBuilder>();
    private final AddendaExtractDef addendaExtractDef;

    AddendaExtractorImpl(AddendaExtractDef addendaExtractDef) {
        this.addendaExtractDef = addendaExtractDef;
    }

    @Override
    public void add(Document document, Croisement croisement) {
        Addenda addenda = document.getAddenda();
        ResultBuilder resultBuilder = builderMap.get(addenda.getSubsetKey());
        DocumentFilter documentFilter = addendaExtractDef.getDocumentFilter();
        if (resultBuilder == null) {
            resultBuilder = new ResultBuilder(addenda);
            builderMap.put(addenda.getSubsetKey(), resultBuilder);
        }
        resultBuilder.add(document, documentFilter, croisement);
    }

    @Override
    public AddendaExtractResult getAddendaExtractResult() {
        AddendaExtractResult.Entry[] array = new AddendaExtractResult.Entry[builderMap.size()];
        int p = 0;
        for (ResultBuilder resultBuilder : builderMap.values()) {
            array[p] = new InternalEntry(resultBuilder.addenda, resultBuilder.getDocumentExtractInfoList());
            p++;
        }
        InternalAddendaExtractResult addendaExtractResult = new InternalAddendaExtractResult(addendaExtractDef, new EntryList(array));
        return addendaExtractResult;
    }


    private class ResultBuilder {

        private Addenda addenda;
        private SortedMap<LiaisonSortKey, InternalDocumentExtractInfo> map = new TreeMap<LiaisonSortKey, InternalDocumentExtractInfo>();

        private ResultBuilder(Addenda addenda) {
            this.addenda = addenda;
        }

        private void add(Document document, DocumentFilter documentFilter, Croisement croisement) {
            int position;
            if (croisement != null) {
                Lien lien = croisement.getLienList().get(0);
                position = CroisementUtils.getPosition(document, croisement.getCroisementKey(), lien);
            } else {
                position = map.size() + 1;
            }
            LiaisonSortKey key = new LiaisonSortKey(document.getId(), position);
            map.put(key, new InternalDocumentExtractInfo(document, documentFilter, croisement));
        }

        private List<DocumentExtractInfo> getDocumentExtractInfoList() {
            return new DocumentExtractInfoList(map.values().toArray(new DocumentExtractInfo[map.size()]));
        }

    }


    private static class InternalAddendaExtractResult implements AddendaExtractResult {

        private final AddendaExtractDef addendaExtractDef;
        private final List<Entry> entryList;

        private InternalAddendaExtractResult(AddendaExtractDef addendaExtractDef, List<Entry> entryList) {
            this.addendaExtractDef = addendaExtractDef;
            this.entryList = entryList;
        }

        @Override
        public AddendaExtractDef getAddendaExtractDef() {
            return addendaExtractDef;
        }

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

    }


    private static class InternalEntry implements AddendaExtractResult.Entry {

        private final Addenda addenda;
        private final List<DocumentExtractInfo> list;

        private InternalEntry(Addenda addenda, List<DocumentExtractInfo> list) {
            this.addenda = addenda;
            this.list = list;
        }

        @Override
        public Addenda getAddenda() {
            return addenda;
        }

        @Override
        public List<DocumentExtractInfo> getDocumentExtractInfoList() {
            return list;
        }

    }


    private static class InternalDocumentExtractInfo implements DocumentExtractInfo {

        private final Document document;
        private final DocumentFilter documentFilter;
        private final Croisement croisement;

        public InternalDocumentExtractInfo(Document document, DocumentFilter documentFilter, Croisement croisement) {
            this.document = document;
            this.documentFilter = documentFilter;
            this.croisement = croisement;
        }

        @Override
        public Document getDocument() {
            return document;
        }

        @Override
        public DocumentFilter getDocumentFilter() {
            return documentFilter;
        }

        @Override
        public Croisement getCroisement() {
            return croisement;
        }

    }


    private static class EntryList extends AbstractList<AddendaExtractResult.Entry> implements RandomAccess {

        private final AddendaExtractResult.Entry[] array;

        private EntryList(AddendaExtractResult.Entry[] array) {
            this.array = array;
        }

        @Override
        public int size() {
            return array.length;
        }

        @Override
        public AddendaExtractResult.Entry get(int index) {
            return array[index];
        }

    }


    private static class DocumentExtractInfoList extends AbstractList<DocumentExtractInfo> implements RandomAccess {

        private final DocumentExtractInfo[] array;

        private DocumentExtractInfoList(DocumentExtractInfo[] array) {
            this.array = array;
        }

        @Override
        public int size() {
            return array.length;
        }

        @Override
        public DocumentExtractInfo get(int index) {
            return array[index];
        }

    }

}
