/* UtilLib - Copyright (c) 2008-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.mapeadores.util.text.lexie;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.localisation.LocalisationLexieFilters;
import net.mapeadores.util.text.SubstringPosition;
import net.mapeadores.util.text.collation.CollationUnit;
import net.mapeadores.util.text.collation.map.SortedCollatedKeyMap;


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

    private final SortedCollatedKeyMap<InternalLexieUnit> collatedKeyMap;
    private final LexieFilter lexieFilter;
    private final Map<String, ParsedLexieSource> lexieSourceMap = new LinkedHashMap<String, ParsedLexieSource>();

    public LexieDistributionBuilder(Lang lang) {
        this.collatedKeyMap = new SortedCollatedKeyMap<InternalLexieUnit>(lang.toLocale());
        this.lexieFilter = LocalisationLexieFilters.getLexieFilter(lang);
    }

    public void addLexieSource(LexieSource lexieSource) {
        InternalParsedLexieSource ipls = new InternalParsedLexieSource(lexieSource);
        lexieSourceMap.put(lexieSource.getSourceId(), ipls);
        InternalLexieParseHandler lexieParseHandler = new InternalLexieParseHandler(ipls);
        LexieParser.parse(lexieSource.getSourceText(), lexieParseHandler);
    }

    public LexieDistribution toLexieDistribution() {
        TextLexieUnit[] textLexieUnitArray = collatedKeyMap.values().toArray(new TextLexieUnit[collatedKeyMap.size()]);
        return new InternalLexieDistribution(textLexieUnitArray, lexieSourceMap);
    }


    private class InternalLexieParseHandler implements LexieParseHandler {

        private final InternalParsedLexieSource parsedLexieSource;

        private InternalLexieParseHandler(InternalParsedLexieSource parsedLexieSource) {
            this.parsedLexieSource = parsedLexieSource;
        }

        @Override
        public void flushLexie(String lexie, int lexieStartIndex) {
            if ((lexieFilter != null) && (!lexieFilter.acceptLexie(lexie))) {
                return;
            }
            String collatedLexie = CollationUnit.collate(lexie, collatedKeyMap.getCollator());
            InternalLexieUnit lexieUnit = collatedKeyMap.getValueByCollatedKey(collatedLexie);
            if (lexieUnit == null) {
                lexieUnit = new InternalLexieUnit(collatedLexie, collatedKeyMap.size() + 1);
                collatedKeyMap.putValueByCollatedKey(collatedLexie, lexieUnit);
            }
            SubstringPosition substringPosition = new SubstringPosition(lexieStartIndex, lexie.length());
            InternalOccurrence implOccurence = new InternalOccurrence(lexieUnit, parsedLexieSource, substringPosition);
            parsedLexieSource.addOccurrence(implOccurence);
            lexieUnit.addOccurence(implOccurence);
            lexieUnit.checkCanonical(lexie);
        }

        @Override
        public void checkSpecialChar(char carac) {
        }

    }


    private static class InternalLexieDistribution implements LexieDistribution {

        private final TextLexieUnit[] textLexieUnitArray;
        private final ParsedLexieSource[] lexieSourceArray;
        private final Map<String, ParsedLexieSource> lexieSourceMap;

        private InternalLexieDistribution(TextLexieUnit[] textLexieUnitArray, Map<String, ParsedLexieSource> lexieSourceMap) {
            this.textLexieUnitArray = textLexieUnitArray;
            this.lexieSourceArray = lexieSourceMap.values().toArray(new ParsedLexieSource[lexieSourceMap.size()]);
            this.lexieSourceMap = lexieSourceMap;
        }

        @Override
        public int getLexieUnitCount() {
            return textLexieUnitArray.length;
        }

        @Override
        public TextLexieUnit getLexieUnit(int index) {
            return textLexieUnitArray[index];
        }

        @Override
        public int getParserLexieSourceCount() {
            return lexieSourceArray.length;
        }

        @Override
        public ParsedLexieSource getParsedLexieSourceByIndex(int index) {
            return lexieSourceArray[index];
        }

        @Override
        public ParsedLexieSource getParsedLexieSourceBySourceId(String sourceId) {
            return lexieSourceMap.get(sourceId);
        }

    }


    private static class InternalParsedLexieSource implements ParsedLexieSource {

        private final LexieSource lexieSource;
        private final List<Occurrence> occurrenceList = new ArrayList<Occurrence>();

        private InternalParsedLexieSource(LexieSource lexieSource) {
            this.lexieSource = lexieSource;
        }

        @Override
        public LexieSource getLexieSource() {
            return lexieSource;
        }

        @Override
        public int getOccurrenceCount() {
            return occurrenceList.size();
        }

        @Override
        public Occurrence getOccurrence(int index) {
            return occurrenceList.get(index);
        }

        private void addOccurrence(Occurrence occurrence) {
            occurrenceList.add(occurrence);
        }

    }


    private static class InternalOccurrence implements Occurrence {

        private final InternalLexieUnit implLexieUnit;
        private final SubstringPosition substringPosition;
        private final InternalParsedLexieSource implParsedLexieSource;
        private int occurrencePosition;

        private InternalOccurrence(InternalLexieUnit implLexieUnit, InternalParsedLexieSource implParsedLexieSource, SubstringPosition substringPosition) {
            this.implLexieUnit = implLexieUnit;
            this.substringPosition = substringPosition;
            this.implParsedLexieSource = implParsedLexieSource;
        }

        @Override
        public TextLexieUnit getLexieUnit() {
            return implLexieUnit;
        }

        @Override
        public SubstringPosition getSubstringPosition() {
            return substringPosition;
        }

        @Override
        public ParsedLexieSource getParsedLexieSource() {
            return implParsedLexieSource;
        }

        @Override
        public int getOccurrencePosition() {
            return occurrencePosition;
        }

        private void setOccurencePosition(int occurencePosition) {
            this.occurrencePosition = occurencePosition;
        }

    }


    private static class InternalLexieUnit implements TextLexieUnit {

        private final String collatedLexie;
        private final List<Occurrence> occurrenceList = new ArrayList<Occurrence>();
        private final String lexieId;
        private String canonicalLexie;

        private InternalLexieUnit(String collatedLexie, int id) {
            this.collatedLexie = collatedLexie;
            this.lexieId = String.valueOf(id);
        }

        @Override
        public String getCollatedLexie() {
            return collatedLexie;
        }

        @Override
        public String getCanonicalLexie() {
            return canonicalLexie;
        }

        @Override
        public String getLexieId() {
            return lexieId;
        }

        @Override
        public int getOccurrenceCount() {
            return occurrenceList.size();
        }

        @Override
        public Occurrence getOccurrence(int index) {
            return occurrenceList.get(index);
        }

        private void addOccurence(InternalOccurrence occurence) {
            int occurencePosition = 1;
            if (occurrenceList.size() > 0) {
                InternalOccurrence last = (InternalOccurrence) occurrenceList.get(occurrenceList.size() - 1);
                if (last.getParsedLexieSource().equals(occurence.getParsedLexieSource())) {
                    occurencePosition = last.getOccurrencePosition() + 1;
                }
            }
            occurence.setOccurencePosition(occurencePosition);
            occurrenceList.add(occurence);
        }

        private void checkCanonical(String lexie) {
            if (canonicalLexie == null) {
                canonicalLexie = lexie;
            } else {
                if (lexie.length() < canonicalLexie.length()) {
                    canonicalLexie = lexie;
                }
            }
        }

    }

}
