/* 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.TreeMap;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.FicheMeta;
import net.fichotheque.croisement.Croisement;
import net.fichotheque.extraction.def.CorpusExtractDef;
import net.fichotheque.extraction.def.FicheFilter;
import net.fichotheque.extraction.run.CorpusExtractResult;
import net.fichotheque.extraction.run.CorpusExtractor;
import net.fichotheque.extraction.run.FicheGroup;
import net.fichotheque.tools.extraction.corpus.GroupEngine;


/**
 *
 * @author Vincent Calame
 */
class CorpusExtractorImpl implements CorpusExtractor {

    private final ResultBuilder namedResultBuilder;
    private final Map<SubsetKey, ResultBuilder> corpusResultBuilderMap;
    private final CorpusExtractDef corpusExtractDef;

    CorpusExtractorImpl(CorpusExtractDef corpusExtractDef) {
        this.corpusExtractDef = corpusExtractDef;
        String extractName = corpusExtractDef.getName();
        if (extractName != null) {
            namedResultBuilder = new ResultBuilder(null);
            corpusResultBuilderMap = null;
        } else {
            namedResultBuilder = null;
            corpusResultBuilderMap = new TreeMap<SubsetKey, ResultBuilder>();
        }
    }

    @Override
    public void add(FicheMeta ficheMeta, Croisement croisement) {
        if (namedResultBuilder != null) {
            namedResultBuilder.add(ficheMeta, corpusExtractDef.getFicheFilter(), croisement);
        } else {
            Corpus corpus = ficheMeta.getCorpus();
            ResultBuilder resultBuilder = corpusResultBuilderMap.get(corpus.getSubsetKey());
            if (resultBuilder == null) {
                resultBuilder = new ResultBuilder(corpus);
                corpusResultBuilderMap.put(corpus.getSubsetKey(), resultBuilder);
            }
            resultBuilder.add(ficheMeta, corpusExtractDef.getFicheFilter(), croisement);
        }
    }

    @Override
    public CorpusExtractResult getCorpusExtractResult() {
        if (namedResultBuilder != null) {
            return new NamedCorpusExtractResult(corpusExtractDef, namedResultBuilder.getGroupList());
        } else {
            CorpusExtractResult.Entry[] array = new CorpusExtractResult.Entry[corpusResultBuilderMap.size()];
            int p = 0;
            for (ResultBuilder resultBuilder : corpusResultBuilderMap.values()) {
                array[p] = new InternalEntry(resultBuilder.corpus, resultBuilder.getGroupList());
                p++;
            }
            return new UnnamedCorpusExtractResult(corpusExtractDef, new EntryList(array));
        }
    }


    private class ResultBuilder {

        private final Corpus corpus;
        private final GroupEngine groupEngine;

        private ResultBuilder(Corpus corpus) {
            this.corpus = corpus;
            this.groupEngine = GroupEngine.newInstance(corpusExtractDef.getGroupClause());
        }

        private void add(FicheMeta ficheMeta, FicheFilter ficheFilter, Croisement croisement) {
            groupEngine.add(ficheMeta, ficheFilter, croisement);
        }

        private List<FicheGroup> getGroupList() {
            return new FicheGroupList(groupEngine.toFicheGroupArray());
        }

    }


    private static abstract class AbstractCorpusExtractResult implements CorpusExtractResult {

        private final CorpusExtractDef corpusExtractDef;


        private AbstractCorpusExtractResult(CorpusExtractDef corpusExtractDef) {
            this.corpusExtractDef = corpusExtractDef;
        }

        @Override
        public CorpusExtractDef getCorpusExtractDef() {
            return corpusExtractDef;
        }

    }


    private static class NamedCorpusExtractResult extends AbstractCorpusExtractResult implements CorpusExtractResult.Named {

        private final List<FicheGroup> groupList;

        private NamedCorpusExtractResult(CorpusExtractDef corpusExtractDef, List<FicheGroup> groupList) {
            super(corpusExtractDef);
            this.groupList = groupList;
        }

        @Override
        public List<FicheGroup> getGroupList() {
            return groupList;
        }

        @Override
        public boolean isEmpty() {
            return groupList.isEmpty();
        }

    }


    private static class UnnamedCorpusExtractResult extends AbstractCorpusExtractResult implements CorpusExtractResult.Unnamed {

        private final List<CorpusExtractResult.Entry> entryList;

        private UnnamedCorpusExtractResult(CorpusExtractDef corpusExtractDef, List<CorpusExtractResult.Entry> entryList) {
            super(corpusExtractDef);
            this.entryList = entryList;
        }

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

        @Override
        public boolean isEmpty() {
            return entryList.isEmpty();
        }

    }


    private static class InternalEntry implements CorpusExtractResult.Entry {

        private final Corpus corpus;
        private final List<FicheGroup> groupList;

        private InternalEntry(Corpus corpus, List<FicheGroup> groupList) {
            this.corpus = corpus;
            this.groupList = groupList;
        }

        @Override
        public Corpus getCorpus() {
            return corpus;
        }

        @Override
        public List<FicheGroup> getGroupList() {
            return groupList;
        }

    }


    private static class FicheGroupList extends AbstractList<FicheGroup> implements RandomAccess {

        private final FicheGroup[] array;

        private FicheGroupList(FicheGroup[] array) {
            this.array = array;
        }

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

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

    }


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

        private final CorpusExtractResult.Entry[] array;

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

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

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

    }

}
