/* FichothequeLib_Tools - Copyright (c) 2011-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.format.formatters;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.fichotheque.Subset;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.fiche.FicheItem;
import net.fichotheque.corpus.metadata.CorpusField;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.format.FichothequeFormatConstants;
import net.fichotheque.format.FormatContext;
import net.fichotheque.format.FormatSource;
import net.fichotheque.format.Tokenizer;
import net.fichotheque.format.Tokens;
import net.fichotheque.format.formatters.FicheItemFormatter;
import net.fichotheque.format.formatters.SourceFormatter;
import net.fichotheque.pointeurs.FichePointeur;
import net.fichotheque.pointeurs.SubsetItemPointeur;
import net.fichotheque.tools.format.GlobalToken;
import net.fichotheque.tools.format.posttransform.Posttransformer;
import net.fichotheque.tools.format.tokenizers.FicheItemTokenizer;
import net.fichotheque.tools.format.tokenizers.TokenizerProvider;
import net.fichotheque.utils.FichothequeFormatUtils;
import net.mapeadores.util.format.FormatConstants;
import net.mapeadores.util.format.FormatDef;
import net.mapeadores.util.format.FormatUtils;
import net.mapeadores.util.logging.LogUtils;


/**
 *
 * @author Vincent Calame
 */
public class FormatDefSourceFormatter implements SourceFormatter {

    private final Subset subset;
    private final FieldKey defaultPropKey;
    private final int position;
    private final int limit;
    private final int maxLength;
    private final int fixedLength;
    private final char fixedChar;
    private final boolean fixedEmpty;
    private final boolean withUniqueTest;
    private final String defaultValue;
    private final FormatDef formatDef;
    private final TokenizerProvider.ByLang byLangProvider;
    private final String defaultSeparator;
    private final Posttransformer posttransformer;
    private final String prefix;
    private final String suffix;
    private FicheItemFormatter defaultPropItemFormatter;

    private FormatDefSourceFormatter(Subset subset, FormatDef formatDef, TokenizerProvider.ByLang byLangProvider, String defaultSeparator) {
        this.subset = subset;
        this.defaultPropKey = FichothequeFormatUtils.toFieldKey(formatDef.getParameterValue(FichothequeFormatConstants.DEFAULTPROP_PARAMKEY));
        this.position = FormatUtils.getInt(formatDef, FormatConstants.POSITION_PARAMKEY);
        this.limit = FormatUtils.getInt(formatDef, FormatConstants.LIMIT_PARAMKEY);
        this.maxLength = FormatUtils.getInt(formatDef, FormatConstants.MAXLENGTH_PARAMKEY);
        this.fixedLength = FormatUtils.getInt(formatDef, FormatConstants.FIXEDLENGTH_PARAMKEY);
        this.fixedChar = FormatUtils.getChar(formatDef, FormatConstants.FIXEDCHAR_PARAMKEY);
        this.fixedEmpty = FormatUtils.getBoolean(formatDef, FormatConstants.FIXEDEMPTY_PARAMKEY);
        this.defaultValue = FormatUtils.getString(formatDef, FormatConstants.DEFAULTVALUE_PARAMKEY);
        this.withUniqueTest = FormatUtils.getBoolean(formatDef, FormatConstants.UNIQUETEST_PARAMKEY);
        String posttransform = FormatUtils.getString(formatDef, FormatConstants.POSTTRANSFORM_PARAMKEY);
        this.posttransformer = Posttransformer.getPosttransformer(posttransform);
        this.prefix = FormatUtils.getPrefix(formatDef);
        this.suffix = FormatUtils.getSuffix(formatDef);
        this.formatDef = formatDef;
        this.byLangProvider = byLangProvider;
        this.defaultSeparator = defaultSeparator;
    }

    private void endInit(FormatContext formatContext) {
        if (subset instanceof Corpus) {
            if (defaultPropKey != null) {
                CorpusField corpusField = ((Corpus) subset).getCorpusMetadata().getCorpusField(defaultPropKey);
                if (corpusField != null) {
                    Tokenizer tokenizer = byLangProvider.getTokenizerList(null).get(0);
                    if (tokenizer instanceof FicheItemTokenizer) {
                        FicheItemTokenizer fifr = (FicheItemTokenizer) tokenizer;
                        if (fifr.isSameType(corpusField.getFicheItemType())) {
                            defaultPropItemFormatter = fifr.getFicheItemFormatter();
                        } else {
                            defaultPropItemFormatter = FicheItemFormatterParser.parse(corpusField, null, formatContext, LogUtils.NULL_MESSAGEHANDLER);
                        }
                    } else {
                        defaultPropItemFormatter = FicheItemFormatterParser.parse(corpusField, null, formatContext, LogUtils.NULL_MESSAGEHANDLER);
                    }

                }
            }
        }
    }

    public static FormatDefSourceFormatter build(Subset subset, FormatDef formatDef, TokenizerProvider.ByLang byLangProvider, String defaultSeparator, FormatContext formatContext) {
        FormatDefSourceFormatter formatDefConverter = new FormatDefSourceFormatter(subset, formatDef, byLangProvider, defaultSeparator);
        formatDefConverter.endInit(formatContext);
        return formatDefConverter;
    }

    @Override
    public String formatSource(FormatSource formatSource) {
        testSubsetItemPointeur(formatSource.getSubsetItemPointeur());
        String result = format(formatSource);
        if (result == null) {
            result = getDefaultString(formatSource);
        }
        if (result != null) {
            if (prefix != null) {
                result = prefix + result;
            }
            if (suffix != null) {
                result = result + suffix;
            }
        }
        if (posttransformer != null) {
            result = posttransformer.posttransform(result);
        }
        if (maxLength > 0) {
            if ((result != null) && (result.length() > maxLength)) {
                result = result.substring(0, maxLength);
            }
        } else if (fixedLength > 0) {
            StringBuilder buf = new StringBuilder();
            int start = 0;
            if (result != null) {
                int currentLength = result.length();
                if ((currentLength == 0) && (!fixedEmpty)) {
                    return "";
                }
                if (currentLength <= fixedLength) {
                    buf.append(result);
                    start = currentLength;
                } else {
                    buf.append(result, 0, fixedLength);
                    start = fixedLength;
                }
            } else if (!fixedEmpty) {
                return null;
            }
            for (int i = start; i < fixedLength; i++) {
                buf.append(fixedChar);
            }
            result = buf.toString();
        }
        return result;
    }

    private void testSubsetItemPointeur(SubsetItemPointeur subsetItemPointeur) {
        if (subset instanceof Corpus) {
            if (!(subsetItemPointeur instanceof FichePointeur)) {
                throw new IllegalArgumentException("subsetItemPointeur must be an instance of FichePointeur");
            }
        }
    }

    private String format(FormatSource formatSource) {
        List<Tokenizer> tokenizerList = byLangProvider.getTokenizerList(formatSource.getDefaultLang());
        int tokenizerLength = tokenizerList.size();
        if (tokenizerLength == 0) {
            return null;
        }
        if (position == FormatConstants.MAX_POSITION) {
            for (int i = (tokenizerLength - 1); i >= 0; i--) {
                Tokenizer tokenizer = tokenizerList.get(i);
                Tokens tokens = tokenizer.tokenize(formatSource);
                int size = tokens.size();
                if (size > 0) {
                    return tokens.get((size - 1));
                }
            }
            return null;
        } else {
            int rangeStart = position;
            int rangeLength = limit;
            if (position != - 1) {
                if (limit == -1) {
                    rangeLength = 1;
                }
            } else {
                rangeStart = 0;
            }
            List<GlobalToken> globalTokenList = new ArrayList<GlobalToken>();
            for (int i = 0; i < tokenizerLength; i++) {
                Tokenizer tokenizer = tokenizerList.get(i);
                Tokens tokens = tokenizer.tokenize(formatSource);
                int size = tokens.size();
                for (int j = 0; j < size; j++) {
                    globalTokenList.add(new GlobalToken(i, tokens, j));
                }
            }
            StringBuilder buf = new StringBuilder();
            int globalTokenLength = globalTokenList.size();
            int formattedCount = 0;
            GlobalToken previousGlobalToken = null;
            Set<String> tokenSet = null;
            if (withUniqueTest) {
                tokenSet = new HashSet<String>();
            }
            for (int i = rangeStart; i < globalTokenLength; i++) {
                GlobalToken globalToken = globalTokenList.get(i);
                String formattedString = globalToken.format();
                if (withUniqueTest) {
                    if (tokenSet.contains(formattedString)) {
                        continue;
                    } else {
                        tokenSet.add(formattedString);
                    }
                }
                if (previousGlobalToken != null) {
                    buf.append(getSeparator(previousGlobalToken, globalToken, formatDef, defaultSeparator));
                }
                buf.append(formattedString);
                formattedCount++;
                previousGlobalToken = globalToken;
                if ((rangeLength > 0) && (formattedCount == rangeLength)) {
                    break;
                }
            }
            if (buf.length() == 0) {
                return null;
            } else {
                return buf.toString();
            }
        }
    }

    private String getDefaultString(FormatSource formatSource) {
        String defaultString = null;
        if (defaultPropItemFormatter != null) {
            Object defObject = ((FichePointeur) formatSource.getSubsetItemPointeur()).getValue(defaultPropKey);
            if (defObject != null) {
                defaultString = defaultPropItemFormatter.formatFicheItem((FicheItem) defObject, formatSource);
                if (defaultString.length() == 0) {
                    defaultString = null;
                }
            }
        }
        if (defaultString == null) {
            defaultString = defaultValue;
        }
        return defaultString;
    }

    private static String getSeparator(GlobalToken firstToken, GlobalToken secondToken, FormatDef formatDef, String defaultSeparator) {
        int firstIndex = firstToken.getTokensIndex();
        int secondIndex = secondToken.getTokensIndex();
        if (firstIndex != secondIndex) {
            return FormatUtils.getSourceSeparator(formatDef, firstIndex, secondIndex, defaultSeparator);
        } else {
            return FormatUtils.getInnerSeparator(formatDef, firstIndex, defaultSeparator);
        }
    }

}
