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

import java.text.ParseException;
import java.util.List;
import net.fichotheque.Fichotheque;
import net.fichotheque.Subset;
import net.fichotheque.SubsetItem;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.importation.TiesImport.TieImport;
import net.fichotheque.include.IncludeKey;
import net.fichotheque.thesaurus.Motcle;
import net.fichotheque.thesaurus.Thesaurus;
import net.fichotheque.thesaurus.policies.DynamicEditPolicy;
import net.fichotheque.thesaurus.policies.PolicyProvider;
import net.fichotheque.tools.importation.ImportationUtils;
import net.fichotheque.tools.importation.TiesImportBuilder;
import net.fichotheque.tools.junction.TieBuffer;
import net.fichotheque.tools.parsers.junction.TieBufferParser;
import net.fichotheque.tools.parsers.junction.WeightMotcleToken;
import net.fichotheque.utils.ThesaurusUtils;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.text.CleanedString;
import net.mapeadores.util.text.Label;
import net.mapeadores.util.text.LabelUtils;
import net.mapeadores.util.text.StringUtils;


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

    public final static short DEFAULT_PARSE = 0;
    public final static short IDALPHA_PARSE = 1;
    public final static short ID_PARSE = 2;
    private final TiesImportBuilder tiesImportBuilder;
    private final Corpus defaultCorpus;
    private final boolean corpusStyle;
    private final Fichotheque fichotheque;
    private final ImportationParseContext parseContext;

    public TiesParser(Subset mainSubset, TiesImportBuilder tiesImportBuilder, ImportationParseContext parseContext) {
        this.tiesImportBuilder = tiesImportBuilder;
        this.fichotheque = mainSubset.getFichotheque();
        if (mainSubset instanceof Corpus) {
            this.defaultCorpus = (Corpus) mainSubset;
            this.corpusStyle = true;
        } else {
            this.defaultCorpus = null;
            this.corpusStyle = false;
        }
        this.parseContext = parseContext;
    }

    public void addRemovedIncludeKey(IncludeKey includeKey) {
        tiesImportBuilder.addRemovedIncludeKey(includeKey);
    }

    public void add(IncludeKey includeKey, List<String> values, boolean replace, short thesaurusParseType) {
        SubsetKey subsetKey = includeKey.getSubsetKey();
        Subset otherSubset = fichotheque.getSubset(subsetKey);
        if (otherSubset == null) {
            return;
        }
        if (subsetKey.isThesaurusSubset()) {
            Thesaurus thesaurus = (Thesaurus) otherSubset;
            add(thesaurus, includeKey, values, replace, thesaurusParseType);
        } else {
            parseIds(otherSubset, includeKey, values, replace);
        }
    }

    private void add(Thesaurus thesaurus, IncludeKey includeKey, List<String> values, boolean replace, short thesaurusParseType) {
        if (thesaurusParseType == IDALPHA_PARSE) {
            if (thesaurus.isIdalphaType()) {
                for (String value : values) {
                    parseIdalpha(thesaurus, includeKey, value, replace);
                }
            } else {
                for (String value : values) {
                    parseId(thesaurus, includeKey, value, replace, true);
                }
            }
        } else if ((thesaurusParseType == DEFAULT_PARSE) && (corpusStyle)) {
            Lang thesaurusLang = ThesaurusUtils.checkDisponibility(parseContext.getThesaurusLangChecker(), thesaurus, parseContext.getWorkingLang());
            DynamicEditPolicy dynamicEditPolicy = getDynamicEditPolicy(thesaurus);
            for (String value : values) {
                parseMotcle(thesaurus, includeKey, value, thesaurusLang, dynamicEditPolicy, replace);
            }
        } else {
            parseIds(thesaurus, includeKey, values, replace);
        }
    }

    public void addLiage(List<String> values, boolean replace) {
        for (String value : values) {
            parseLiage(value, replace);
        }
    }

    private void parseLiage(String value, boolean replace) {
        String[] tokens = StringUtils.getTechnicalTokens(value, false);
        for (String token : tokens) {
            try {
                TieBuffer tieBuffer = TieBufferParser.parse(parseContext.getFichothequeQuestioner(), token, SubsetKey.CATEGORY_CORPUS, defaultCorpus, "");
                SubsetItem otherSubsetItem = tieBuffer.getSubsetItem();
                Subset otherSubset = otherSubsetItem.getSubset();
                IncludeKey.newInstance(otherSubset.getSubsetKey());
                TieImport tieImport = toTieImport(IncludeKey.newInstance(otherSubset.getSubsetKey()), otherSubsetItem, tieBuffer.getWeight());
                if (replace) {
                    tiesImportBuilder.addReplaceTieImport(tieImport);
                } else {
                    tiesImportBuilder.addAppendTieImport(tieImport);
                }
            } catch (ParseException pe) {
            }
        }
    }

    private void parseIds(Subset otherSubset, IncludeKey includeKey, List<String> values, boolean replace) {
        for (String value : values) {
            parseId(otherSubset, includeKey, value, replace, false);
        }
    }

    private void parseId(Subset otherSubset, IncludeKey includeKey, String value, boolean replace, boolean checkUndescore) {
        int weightFilter = includeKey.getWeightFilter();
        String mode = includeKey.getMode();
        String[] tokens = StringUtils.getTechnicalTokens(value, false);
        for (String token : tokens) {
            if ((checkUndescore) && (token.startsWith("_"))) {
                token = token.substring(1);
            }
            try {
                TieBuffer tieBuffer = TieBufferParser.parseId(otherSubset, token, mode, weightFilter);
                TieImport tieImport = toTieImport(includeKey, tieBuffer);
                if (replace) {
                    tiesImportBuilder.addReplaceTieImport(tieImport);
                } else {
                    tiesImportBuilder.addAppendTieImport(tieImport);
                }
            } catch (ParseException pe) {
            }
        }
    }

    private void parseIdalpha(Thesaurus thesaurus, IncludeKey includeKey, String value, boolean replace) {
        int weightFilter = includeKey.getWeightFilter();
        String mode = includeKey.getMode();
        String[] tokens = StringUtils.getTechnicalTokens(value, false);
        for (String token : tokens) {
            try {
                TieBuffer tieBuffer = TieBufferParser.parseIdalpha(thesaurus, token, mode, weightFilter);
                TieImport tieImport = toTieImport(includeKey, tieBuffer);
                if (replace) {
                    tiesImportBuilder.addReplaceTieImport(tieImport);
                } else {
                    tiesImportBuilder.addAppendTieImport(tieImport);
                }
            } catch (ParseException pe) {
            }
        }
    }

    private void parseMotcle(Thesaurus thesaurus, IncludeKey includeKey, String value, Lang thesaurusLang, DynamicEditPolicy dynamicEditPolicy, boolean replace) {
        boolean withIdalpha = thesaurus.isIdalphaType();
        int weightFilter = includeKey.getWeightFilter();
        String mode = includeKey.getMode();
        String[] tokens = StringUtils.getTechnicalTokens(value, ';', false);
        for (String token : tokens) {
            WeightMotcleToken motcleToken = WeightMotcleToken.parse(token, withIdalpha, weightFilter);
            if (motcleToken == null) {
                continue;
            }
            int weight = motcleToken.getWeight();
            Motcle motcle = WeightMotcleToken.getMotcle(motcleToken, thesaurus, thesaurusLang);
            if (motcle != null) {
                TieImport tieImport = toTieImport(includeKey, motcle, weight);
                if (replace) {
                    tiesImportBuilder.addReplaceTieImport(tieImport);
                } else {
                    tiesImportBuilder.addAppendTieImport(tieImport);
                }
            } else {
                CleanedString labelText = motcleToken.getText();
                switch (dynamicEditPolicy.getType()) {
                    case DynamicEditPolicy.ALLOW_TYPE: {
                        if (!motcleToken.isIdBundle()) {
                            Label label = toLabel(thesaurusLang, labelText);
                            TieImport tieImport = ImportationUtils.toTieImport(includeKey, thesaurus, label, weight);
                            if (replace) {
                                tiesImportBuilder.addReplaceTieImport(tieImport);
                            } else {
                                tiesImportBuilder.addAppendTieImport(tieImport);
                            }
                        }
                        break;
                    }
                    case DynamicEditPolicy.TRANSFER_TYPE: {
                        if (!motcleToken.isIdBundle()) {
                            Thesaurus transferThesaurus = ThesaurusUtils.getTransferThesaurus(fichotheque, dynamicEditPolicy);
                            Motcle transferMotcle = getMotcle(transferThesaurus, thesaurusLang, labelText);
                            IncludeKey transferIncludeKey = IncludeKey.newInstance(transferThesaurus.getSubsetKey(), mode, weightFilter);
                            if (transferMotcle == null) {
                                Label label = toLabel(thesaurusLang, labelText);
                                tiesImportBuilder.addAppendTieImport(ImportationUtils.toTieImport(transferIncludeKey, transferThesaurus, label, weight));
                            } else {
                                tiesImportBuilder.addAppendTieImport(toTieImport(transferIncludeKey, transferMotcle, weight));
                            }
                        }
                        break;
                    }
                    case DynamicEditPolicy.CHECK_TYPE: {
                        if (!motcleToken.isIdBundle()) {
                            Thesaurus[] checkThesaurusArray = ThesaurusUtils.getCheckThesaurusArray(fichotheque, dynamicEditPolicy);
                            boolean done = false;
                            for (Thesaurus checkThesaurus : checkThesaurusArray) {
                                Motcle checkMotcle = getMotcle(checkThesaurus, thesaurusLang, labelText);
                                if (checkMotcle != null) {
                                    IncludeKey checkIncludeKey = IncludeKey.newInstance(checkThesaurus.getSubsetKey(), mode, weightFilter);
                                    tiesImportBuilder.addAppendTieImport(toTieImport(checkIncludeKey, checkMotcle, weight));
                                    done = true;
                                    break;
                                }
                            }
                            if (!done) {
                                Label label = toLabel(thesaurusLang, labelText);
                                TieImport tieImport = ImportationUtils.toTieImport(includeKey, thesaurus, label, weight);
                                if (replace) {
                                    tiesImportBuilder.addReplaceTieImport(tieImport);
                                } else {
                                    tiesImportBuilder.addAppendTieImport(tieImport);
                                }
                            }
                        }
                        break;
                    }
                    case DynamicEditPolicy.EXTERNAL_TYPE:
                        TieImport tieImport = null;
                        if (withIdalpha) {
                            if (motcleToken.isIdBundle()) {
                                tieImport = ImportationUtils.toTieImport(includeKey, thesaurus, String.valueOf(motcleToken.getId()), weight);
                            } else {
                                tieImport = ImportationUtils.toTieImport(includeKey, thesaurus, labelText.toString(), weight);
                            }
                        } else {
                            if (motcleToken.isIdBundle()) {
                                tieImport = ImportationUtils.toTieImport(includeKey, thesaurus, motcleToken.getId(), weight);
                            } else {
                                Label label = toLabel(thesaurusLang, labelText);
                                tieImport = ImportationUtils.toTieImport(includeKey, thesaurus, label, weight);
                            }
                        }
                        if (tieImport != null) {
                            if (replace) {
                                tiesImportBuilder.addReplaceTieImport(tieImport);
                            } else {
                                tiesImportBuilder.addAppendTieImport(tieImport);
                            }
                        }
                        break;
                }
            }
        }
    }


    private Label toLabel(Lang thesaurusLang, CleanedString labelString) {
        if (labelString != null) {
            return LabelUtils.toLabel(thesaurusLang, labelString);
        } else {
            return null;
        }

    }

    private Motcle getMotcle(Thesaurus thesaurus, Lang thesaurusLang, CleanedString labelString) {
        if (labelString == null) {
            return null;
        }
        Lang lang = ThesaurusUtils.checkDisponibility(parseContext.getThesaurusLangChecker(), thesaurus, thesaurusLang);
        return thesaurus.seekMotcleByLabel(labelString.toString(), lang);
    }

    private DynamicEditPolicy getDynamicEditPolicy(Thesaurus thesaurus) {
        DynamicEditPolicy dynamicEditPolicy = ThesaurusUtils.NONE_POLICY;
        PolicyProvider policyProvider = parseContext.getPolicyProvider();
        if (policyProvider != null) {
            dynamicEditPolicy = policyProvider.getDynamicEditPolicy(thesaurus);
        }
        return dynamicEditPolicy;
    }

    private TieImport toTieImport(IncludeKey includeKey, TieBuffer tieBuffer) {
        SubsetItem subsetItem = tieBuffer.getSubsetItem();
        return ImportationUtils.toTieImport(includeKey, subsetItem.getSubset(), subsetItem.getId(), tieBuffer.getWeight());
    }

    private TieImport toTieImport(IncludeKey includeKey, SubsetItem subsetItem, int weight) {
        return ImportationUtils.toTieImport(includeKey, subsetItem.getSubset(), subsetItem.getId(), weight);
    }

}
