/* BdfServer - 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 fr.exemole.bdfserver.tools.ficheform.builders.field;

import fr.exemole.bdfserver.api.ficheform.FicheFormParameters;
import fr.exemole.bdfserver.api.ui.FieldUi;
import fr.exemole.bdfserver.tools.L10nUtils;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.RandomAccess;
import net.fichotheque.corpus.fiche.AmountItem;
import net.fichotheque.corpus.fiche.FicheItem;
import net.fichotheque.corpus.fiche.FicheItems;
import net.fichotheque.corpus.metadata.CorpusField;
import net.fichotheque.pointeurs.FichePointeur;
import net.fichotheque.syntax.FormSyntax;
import net.fichotheque.utils.FicheUtils;
import net.mapeadores.util.attr.AttributeUtils;
import net.mapeadores.util.attr.Attributes;
import net.mapeadores.util.money.Currencies;
import net.mapeadores.util.money.ExtendedCurrency;
import net.mapeadores.util.text.SeparatorOptions;
import fr.exemole.bdfserver.api.ficheform.field.AmountInfoEntry;


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

    private final CorpusField corpusField;
    private final Map<ExtendedCurrency, String> currencyMap = new LinkedHashMap<ExtendedCurrency, String>();
    private String label = "";
    private boolean mandatory = false;
    private String othersValue = "";
    private Attributes attributes = AttributeUtils.EMPTY_ATTRIBUTES;

    public AmountInfoEntryBuilder(CorpusField corpusField) {
        this.corpusField = corpusField;
        Currencies currencies = corpusField.getCurrencies();
        if (currencies == null) {
            throw new IllegalArgumentException("corpusField.getFieldOptions().getCurrencies() is null");
        }
        for (ExtendedCurrency currency : currencies) {
            currencyMap.put(currency, "");
        }
    }

    public AmountInfoEntryBuilder setLabel(String label) {
        if (label == null) {
            throw new IllegalArgumentException("label is null");
        }
        this.label = label;
        return this;
    }

    public AmountInfoEntryBuilder setMandatory(boolean mandatory) {
        this.mandatory = mandatory;
        return this;
    }

    public AmountInfoEntryBuilder setAttributes(Attributes attributes) {
        if (attributes == null) {
            this.attributes = AttributeUtils.EMPTY_ATTRIBUTES;
        } else {
            this.attributes = attributes;
        }
        return this;
    }

    public AmountInfoEntryBuilder putCurrency(ExtendedCurrency currency, String amountValue) {
        if (amountValue == null) {
            throw new IllegalArgumentException("amountValue is null");
        }
        if (currencyMap.containsKey(currency)) {
            currencyMap.put(currency, amountValue);
        }
        return this;
    }

    public AmountInfoEntryBuilder setOthersValue(String othersValue) {
        if (othersValue == null) {
            throw new IllegalArgumentException("othersValue is null");
        }
        this.othersValue = othersValue;
        return this;
    }

    private AmountInfoEntryBuilder setCurrencyFromFicheItems(FicheItems ficheItems, FicheFormParameters ficheFormParameters) {
        if (ficheItems == null) {
            return this;
        }
        FormSyntax.Parameters syntaxParameters = ficheFormParameters.getFicheItemFormSyntaxParameters();
        List<FicheItem> othersList = new ArrayList<FicheItem>();
        char decimalChar = syntaxParameters.decimalChar();
        for (FicheItem ficheItem : ficheItems) {
            if (ficheItem instanceof AmountItem) {
                AmountItem amountItem = (AmountItem) ficheItem;
                if (!currencyMap.containsKey(amountItem.getCurrency())) {
                    othersList.add(amountItem);
                } else {
                    currencyMap.put(amountItem.getCurrency(), amountItem.getDecimal().toStringWithBlank(decimalChar));
                }
            } else {
                othersList.add(ficheItem);
            }
        }
        if (!othersList.isEmpty()) {
            boolean isBlock = corpusField.isBlockDisplayInfoField();
            SeparatorOptions separatorOptions = (isBlock) ? ficheFormParameters.getBlockSeparatorOptions() : ficheFormParameters.getInlineSeparatorOptions();
            setOthersValue(FormSyntax.toString(FicheUtils.toFicheItems(othersList), ficheFormParameters.getFichothequeQuestioner(), separatorOptions, syntaxParameters));
        }
        return this;
    }

    public AmountInfoEntry toAmountInfoEntry() {
        int size = currencyMap.size();
        InternalAmountValue[] valueArray = new InternalAmountValue[size];
        int p = 0;
        for (Map.Entry<ExtendedCurrency, String> entry : currencyMap.entrySet()) {
            valueArray[p] = new InternalAmountValue(entry.getKey(), entry.getValue());
            p++;
        }
        return new InternalAmountInfoEntry(corpusField, label, mandatory, attributes, wrap(valueArray), othersValue);
    }

    public static AmountInfoEntryBuilder check(FichePointeur fichePointeur, FicheFormParameters ficheFormParameters, CorpusField corpusField, FieldUi fieldUi) {
        FicheItems ficheItems = (FicheItems) fichePointeur.getValue(corpusField);
        if ((ficheItems == null) && (fieldUi.isObsolete())) {
            return null;
        }
        return init(corpusField)
                .setCurrencyFromFicheItems(ficheItems, ficheFormParameters)
                .setAttributes(fieldUi.getAttributes())
                .setLabel(L10nUtils.toLabelString(ficheFormParameters, corpusField));
    }


    public static AmountInfoEntryBuilder init(CorpusField corpusField) {
        return new AmountInfoEntryBuilder(corpusField);
    }

    private static List<AmountInfoEntry.Value> wrap(AmountInfoEntry.Value[] array) {
        return new EntryList(array);
    }


    private static class InternalAmountInfoEntry implements AmountInfoEntry {

        private final CorpusField corpusField;
        private final String label;
        private final boolean mandatory;
        private final Attributes attributes;
        private final List<AmountInfoEntry.Value> valueList;
        private final String othersValue;

        private InternalAmountInfoEntry(CorpusField corpusField, String label, boolean mandatory, Attributes attributes, List<AmountInfoEntry.Value> valueList, String othersValue) {
            this.corpusField = corpusField;
            this.label = label;
            this.mandatory = mandatory;
            this.attributes = attributes;
            this.valueList = valueList;
            this.othersValue = othersValue;
        }

        @Override
        public CorpusField getCorpusField() {
            return corpusField;
        }

        @Override
        public String getLabel() {
            return label;
        }

        @Override
        public boolean isMandatory() {
            return mandatory;
        }

        @Override
        public Attributes getAttributes() {
            return attributes;
        }

        @Override
        public List<AmountInfoEntry.Value> getValueList() {
            return valueList;
        }

        @Override
        public String getOthersValue() {
            return othersValue;
        }

    }


    private static class InternalAmountValue implements AmountInfoEntry.Value {

        private final ExtendedCurrency currency;
        private final String value;

        private InternalAmountValue(ExtendedCurrency currency, String value) {
            this.currency = currency;
            this.value = value;
        }

        @Override
        public ExtendedCurrency getCurrency() {
            return currency;
        }

        @Override
        public String getAmountValue() {
            return value;
        }

    }


    private static class EntryList extends AbstractList<AmountInfoEntry.Value> implements RandomAccess {

        private final AmountInfoEntry.Value[] array;

        private EntryList(AmountInfoEntry.Value[] array) {
            this.array = array;
        }

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

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

    }

}
