/* FichothequeLib_Tools - Copyright (c) 2013-2018 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.importation;

import java.util.AbstractList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.RandomAccess;
import net.fichotheque.Subset;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.importation.LabelImport;
import net.fichotheque.include.IncludeKey;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.text.CleanedString;


/**
 *
 * @author Vincent Calame
 */
public class LabelImportBuilder {

    private final Map<String, LabelImport.PhraseImport> fichothequePhraseMap = new LinkedHashMap<String, LabelImport.PhraseImport>();
    private final Map<SubsetKey, MetadataImportBuilder> metadataImportBuilderMap = new HashMap<SubsetKey, MetadataImportBuilder>();
    private final Map<SubsetKey, CorpusImportBuilder> corpusImportBuilderMap = new HashMap<SubsetKey, CorpusImportBuilder>();
    private final Lang lang;

    public LabelImportBuilder(Lang lang) {
        this.lang = lang;
    }

    public LabelImport toLabelImport() {
        List<LabelImport.PhraseImport> fichothequePhraseImportList = wrap(fichothequePhraseMap.values());
        LabelImport.MetadataImport[] metadataArray = new LabelImport.MetadataImport[metadataImportBuilderMap.size()];
        int p = 0;
        for (MetadataImportBuilder builder : metadataImportBuilderMap.values()) {
            metadataArray[p] = builder.toMetadataImport();
            p++;
        }
        LabelImport.CorpusImport[] corpusArray = new LabelImport.CorpusImport[corpusImportBuilderMap.size()];
        int q = 0;
        for (CorpusImportBuilder builder : corpusImportBuilderMap.values()) {
            corpusArray[q] = builder.toCorpusImport();
            q++;
        }
        return new InternalLabelImport(lang, fichothequePhraseImportList, wrap(metadataArray), wrap(corpusArray));
    }

    public void addFichothequePhrase(String name, CleanedString value) {
        fichothequePhraseMap.put(name, new InternalPhraseImport(name, value));
    }

    public void addSubsetPhrase(Subset subset, String name, CleanedString value) {
        SubsetKey subsetKey = subset.getSubsetKey();
        MetadataImportBuilder builder = metadataImportBuilderMap.get(subsetKey);
        if (builder == null) {
            builder = new MetadataImportBuilder(subset);
            metadataImportBuilderMap.put(subsetKey, builder);
        }
        builder.addLabel(name, value);
    }

    public void addFieldKeyImport(Corpus corpus, FieldKey fieldKey, CleanedString value) {
        CorpusImportBuilder builder = getBuilder(corpus);
        builder.addFieldKeyImport(fieldKey, value);
    }

    public void addIncludeKeyImport(Corpus corpus, IncludeKey includeKey, CleanedString value) {
        CorpusImportBuilder builder = getBuilder(corpus);
        builder.addIncludeKeyImport(includeKey, value);
    }

    private CorpusImportBuilder getBuilder(Corpus corpus) {
        CorpusImportBuilder builder = corpusImportBuilderMap.get(corpus.getSubsetKey());
        if (builder == null) {
            builder = new CorpusImportBuilder(corpus);
            corpusImportBuilderMap.put(corpus.getSubsetKey(), builder);
        }
        return builder;
    }


    private static class InternalLabelImport implements LabelImport {

        private final Lang lang;
        private final List<PhraseImport> fichothequePhraseImportList;
        private final List<MetadataImport> metadataImportList;
        private final List<CorpusImport> corpusImportList;


        private InternalLabelImport(Lang lang, List<PhraseImport> fichothequePhraseImportList, List<MetadataImport> metadataImportList, List<CorpusImport> corpusImportList) {
            this.lang = lang;
            this.fichothequePhraseImportList = fichothequePhraseImportList;
            this.metadataImportList = metadataImportList;
            this.corpusImportList = corpusImportList;
        }

        @Override
        public Lang getLang() {
            return lang;
        }

        @Override
        public List<PhraseImport> getFichothequePhraseImportList() {
            return fichothequePhraseImportList;
        }

        @Override
        public List<MetadataImport> getMetadataImportList() {
            return metadataImportList;
        }

        @Override
        public List<CorpusImport> getCorpusImportList() {
            return corpusImportList;
        }

    }


    private static class MetadataImportBuilder {

        private final Subset subset;
        private final Map<String, LabelImport.PhraseImport> phraseMap = new LinkedHashMap<String, LabelImport.PhraseImport>();

        private MetadataImportBuilder(Subset subset) {
            this.subset = subset;
        }

        private void addLabel(String name, CleanedString value) {
            phraseMap.put(name, new InternalPhraseImport(name, value));
        }

        private LabelImport.MetadataImport toMetadataImport() {
            return new InternalMetadataImport(subset, wrap(phraseMap.values()));
        }

    }


    private static class InternalMetadataImport implements LabelImport.MetadataImport {

        private final Subset subset;
        private final List<LabelImport.PhraseImport> phraseImportList;

        private InternalMetadataImport(Subset subset, List<LabelImport.PhraseImport> phraseImportList) {
            this.subset = subset;
            this.phraseImportList = phraseImportList;
        }

        @Override
        public Subset getSubset() {
            return subset;
        }

        @Override
        public List<LabelImport.PhraseImport> getPhraseImportList() {
            return phraseImportList;
        }

    }


    private static class CorpusImportBuilder {

        private final Corpus corpus;
        private final Map<FieldKey, LabelImport.FieldKeyImport> fieldKeyMap = new LinkedHashMap<FieldKey, LabelImport.FieldKeyImport>();
        private final Map<IncludeKey, LabelImport.IncludeKeyImport> includeKeyMap = new LinkedHashMap<IncludeKey, LabelImport.IncludeKeyImport>();

        private CorpusImportBuilder(Corpus corpus) {
            this.corpus = corpus;
        }


        private void addFieldKeyImport(FieldKey fieldKey, CleanedString value) {
            fieldKeyMap.put(fieldKey, new InternalFieldKeyImport(fieldKey, value));
        }

        private void addIncludeKeyImport(IncludeKey includeKey, CleanedString value) {
            includeKeyMap.put(includeKey, new InternalIncludeKeyImport(includeKey, value));
        }

        private LabelImport.CorpusImport toCorpusImport() {
            List<LabelImport.FieldKeyImport> fieldKeyImportList = wrap(fieldKeyMap.values().toArray(new LabelImport.FieldKeyImport[fieldKeyMap.size()]));
            List<LabelImport.IncludeKeyImport> includeKeyImportList = wrap(includeKeyMap.values().toArray(new LabelImport.IncludeKeyImport[includeKeyMap.size()]));
            return new InternalCorpusImport(corpus, fieldKeyImportList, includeKeyImportList);
        }

    }


    private static class InternalCorpusImport implements LabelImport.CorpusImport {

        private final Corpus corpus;
        private final List<LabelImport.FieldKeyImport> fieldKeyImportList;
        private final List<LabelImport.IncludeKeyImport> includeKeyImportList;

        private InternalCorpusImport(Corpus corpus, List<LabelImport.FieldKeyImport> fieldKeyImportList, List<LabelImport.IncludeKeyImport> includeKeyImportList) {
            this.corpus = corpus;
            this.fieldKeyImportList = fieldKeyImportList;
            this.includeKeyImportList = includeKeyImportList;
        }

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

        @Override
        public List<LabelImport.FieldKeyImport> getFieldKeyImportList() {
            return fieldKeyImportList;

        }

        @Override
        public List<LabelImport.IncludeKeyImport> getIncludeKeyImportList() {
            return includeKeyImportList;
        }

    }


    private static class InternalPhraseImport implements LabelImport.PhraseImport {

        private final String name;
        private final CleanedString labelString;

        private InternalPhraseImport(String name, CleanedString labelString) {
            this.name = name;
            this.labelString = labelString;
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public CleanedString getLabelString() {
            return labelString;
        }

    }


    private static class InternalFieldKeyImport implements LabelImport.FieldKeyImport {

        private final FieldKey fieldKey;
        private final CleanedString labelString;

        private InternalFieldKeyImport(FieldKey fieldKey, CleanedString labelString) {
            this.fieldKey = fieldKey;
            this.labelString = labelString;
        }

        @Override
        public FieldKey getFieldKey() {
            return fieldKey;
        }

        @Override
        public CleanedString getLabelString() {
            return labelString;
        }

    }


    private static class InternalIncludeKeyImport implements LabelImport.IncludeKeyImport {

        private final IncludeKey includeKey;
        private final CleanedString labelString;

        private InternalIncludeKeyImport(IncludeKey includeKey, CleanedString labelString) {
            this.includeKey = includeKey;
            this.labelString = labelString;
        }

        @Override
        public IncludeKey getIncludeKey() {
            return includeKey;
        }

        @Override
        public CleanedString getLabelString() {
            return labelString;
        }

    }

    private static List<LabelImport.PhraseImport> wrap(Collection<LabelImport.PhraseImport> phraseImports) {
        return wrap(phraseImports.toArray(new LabelImport.PhraseImport[phraseImports.size()]));
    }

    public static List<LabelImport.PhraseImport> wrap(LabelImport.PhraseImport[] array) {
        return new PhraseImportList(array);
    }


    private static class PhraseImportList extends AbstractList<LabelImport.PhraseImport> implements RandomAccess {

        private final LabelImport.PhraseImport[] array;

        private PhraseImportList(LabelImport.PhraseImport[] array) {
            this.array = array;
        }

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

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

    }

    public static List<LabelImport.MetadataImport> wrap(LabelImport.MetadataImport[] array) {
        return new MetadataImportList(array);
    }


    private static class MetadataImportList extends AbstractList<LabelImport.MetadataImport> implements RandomAccess {

        private final LabelImport.MetadataImport[] array;

        private MetadataImportList(LabelImport.MetadataImport[] array) {
            this.array = array;
        }

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

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

    }

    public static List<LabelImport.CorpusImport> wrap(LabelImport.CorpusImport[] array) {
        return new CorpusImportList(array);
    }


    private static class CorpusImportList extends AbstractList<LabelImport.CorpusImport> implements RandomAccess {

        private final LabelImport.CorpusImport[] array;

        private CorpusImportList(LabelImport.CorpusImport[] array) {
            this.array = array;
        }

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

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

    }


    public static List<LabelImport.FieldKeyImport> wrap(LabelImport.FieldKeyImport[] array) {
        return new FieldKeyImportList(array);
    }


    private static class FieldKeyImportList extends AbstractList<LabelImport.FieldKeyImport> implements RandomAccess {

        private final LabelImport.FieldKeyImport[] array;

        private FieldKeyImportList(LabelImport.FieldKeyImport[] array) {
            this.array = array;
        }

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

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

    }


    public static List<LabelImport.IncludeKeyImport> wrap(LabelImport.IncludeKeyImport[] array) {
        return new IncludeKeyImportList(array);
    }


    private static class IncludeKeyImportList extends AbstractList<LabelImport.IncludeKeyImport> implements RandomAccess {

        private final LabelImport.IncludeKeyImport[] array;

        private IncludeKeyImportList(LabelImport.IncludeKeyImport[] array) {
            this.array = array;
        }

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

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

    }

}
