/* BdfServer - Copyright (c) 2009-2026 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;

import fr.exemole.bdfserver.api.BdfServerConstants;
import fr.exemole.bdfserver.api.ficheform.FicheFormParameters;
import fr.exemole.bdfserver.api.ficheform.include.SubsetItemState;
import fr.exemole.bdfserver.api.ficheform.include.SubsetItemValue;
import fr.exemole.bdfserver.api.instruction.BdfParameters;
import fr.exemole.bdfserver.api.instruction.OutputParameters;
import fr.exemole.bdfserver.api.namespaces.RoleSpace;
import fr.exemole.bdfserver.api.ui.FieldUi;
import fr.exemole.bdfserver.api.ui.IncludeUi;
import fr.exemole.bdfserver.api.ui.SpecialIncludeUi;
import fr.exemole.bdfserver.api.ui.SubsetIncludeUi;
import fr.exemole.bdfserver.api.ui.UiComponent;
import fr.exemole.bdfserver.tools.BdfServerUtils;
import java.util.AbstractList;
import java.util.Collection;
import java.util.List;
import java.util.RandomAccess;
import net.fichotheque.FichothequeConstants;
import net.fichotheque.Subset;
import net.fichotheque.SubsetItem;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.fiche.FicheBlocks;
import net.fichotheque.corpus.fiche.FicheItem;
import net.fichotheque.corpus.fiche.FicheItems;
import net.fichotheque.corpus.metadata.CorpusField;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.include.ExtendedIncludeKey;
import net.fichotheque.junction.Liaison;
import net.fichotheque.namespaces.BdfSpace;
import net.fichotheque.permission.PermissionSummary;
import net.fichotheque.pointeurs.FichePointeur;
import net.fichotheque.pointeurs.SubsetItemPointeur;
import net.fichotheque.sphere.Redacteur;
import net.fichotheque.syntax.FicheblockSyntax;
import net.fichotheque.syntax.FormSyntax;
import net.fichotheque.thesaurus.Motcle;
import net.fichotheque.thesaurus.Thesaurus;
import net.mapeadores.util.attr.Attribute;
import net.mapeadores.util.exceptions.SwitchException;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.request.RequestMap;
import net.mapeadores.util.text.SeparatorOptions;


/**
 *
 * @author Vincent Calame
 */
public final class FicheFormUtils {

    private FicheFormUtils() {
    }

    public static SubsetItemPointeur checkMasterPointeur(FichePointeur fichePointeur, FicheFormParameters ficheFormParameters, SubsetIncludeUi includeUi) {
        ExtendedIncludeKey extentedIncludeKey = includeUi.getExtendedIncludeKey();
        if (extentedIncludeKey.isMaster()) {
            Subset masterSubset = fichePointeur.getCorpus().getMasterSubset();
            if (masterSubset != null) {
                SubsetItemPointeur masterPointeur = fichePointeur.getParentagePointeur(masterSubset.getSubsetKey());
                if (masterPointeur.isEmpty()) {
                    masterPointeur.setCurrentSubsetItem(ficheFormParameters.getMasterSubsetItem());
                }
                return masterPointeur;
            } else {
                return fichePointeur;
            }

        } else {
            return fichePointeur;
        }
    }

    public static String getStringValue(FichePointeur fichePointeur, CorpusField corpusField, FicheFormParameters ficheFormParameters) {
        Object obj = fichePointeur.getValue(corpusField);
        if (obj == null) {
            return "";
        }
        if (obj instanceof Integer) {
            return obj.toString();
        }
        if (obj instanceof String) {
            return FormSyntax.escapeString((String) obj);
        }
        if (obj instanceof FicheItem) {
            return FormSyntax.toString((FicheItem) obj, ficheFormParameters.getFichothequeQuestioner(), getParameters(ficheFormParameters, corpusField));
        }
        if (obj instanceof FicheItems) {
            boolean isBlock = corpusField.isBlockDisplayInfoField();
            SeparatorOptions separatorOptions = (isBlock) ? ficheFormParameters.getBlockSeparatorOptions() : ficheFormParameters.getInlineSeparatorOptions();
            return FormSyntax.toString((FicheItems) obj, ficheFormParameters.getFichothequeQuestioner(), separatorOptions, getParameters(ficheFormParameters, corpusField));
        }
        if (obj instanceof FicheBlocks) {
            return FicheblockSyntax.toString((FicheBlocks) obj, ficheFormParameters.getFicheBlockFormSyntaxParameters());
        }
        throw new SwitchException("Unexpected instance of " + obj.getClass().getName());
    }

    public static FormSyntax.Parameters getParameters(FicheFormParameters ficheFormParameters, CorpusField corpusField) {
        FormSyntax.Parameters currentParameters = ficheFormParameters.getFicheItemFormSyntaxParameters();
        SubsetKey defaultSphereKey = corpusField.getDefaultSphereKey();
        if (defaultSphereKey == null) {
            return currentParameters;
        }
        return FormSyntax.parameters(currentParameters).defautSphereKey(defaultSphereKey);
    }

    public static String getStringValue(Thesaurus thesaurus, Collection<Liaison> motcleLiaisonList, SubsetIncludeUi includeUi, FicheFormParameters ficheFormParameters) {
        if (motcleLiaisonList.isEmpty()) {
            return "";
        }
        boolean ignoreWeight = includeUi.getExtendedIncludeKey().hasWeightFilter();
        boolean withIdalpha = thesaurus.isIdalphaType();
        Lang lang = BdfServerUtils.checkLangDisponibility(ficheFormParameters.getBdfServer(), thesaurus, ficheFormParameters.getWorkingLang());
        SeparatorOptions separatorOptions = ficheFormParameters.getInlineSeparatorOptions();
        StringBuilder buf = new StringBuilder();
        boolean next = false;
        for (Liaison liaison : motcleLiaisonList) {
            if (next) {
                appendSeparator(buf, separatorOptions);
            } else {
                next = true;
            }
            Motcle motcle = (Motcle) liaison.getSubsetItem();
            int weight = liaison.getTie().getWeight();
            if (withIdalpha) {
                buf.append(motcle.getIdalpha());
            } else {
                buf.append(motcle.getLabelString(lang, String.valueOf(motcle.getId())));
            }
            if (!ignoreWeight) {
                appendWeight(buf, weight);
            }
        }
        if (separatorOptions.isLastInclude()) {
            appendSeparator(buf, separatorOptions);
        }
        return buf.toString();
    }

    private static void appendWeight(StringBuilder buf, int weight) {
        if (weight > 1) {
            switch (weight) {
                case 2:
                    buf.append('+');
                    break;
                case 3:
                    buf.append("++");
                    break;
                case 4:
                    buf.append("+++");
                    break;
                default:
                    buf.append(" <");
                    buf.append(weight);
                    buf.append('>');
                    break;
            }
        }
    }

    private static void appendSeparator(StringBuilder stringBuffer, SeparatorOptions separatorOptions) {
        stringBuffer.append(separatorOptions.getSeparator());
        if (separatorOptions.isSpaceInclude()) {
            stringBuffer.append(' ');
        }
    }

    public static String getWidth(UiComponent uiComponent) {
        String width = uiComponent.getOptionValue(BdfServerConstants.INPUTWIDTH_OPTION);
        if (!width.isEmpty()) {
            try {
                width = FichothequeConstants.checkWidth(width);
            } catch (IllegalArgumentException iae) {
                width = "";
            }
        }
        if (width.isEmpty()) {
            if (uiComponent instanceof FieldUi) {
                return getDefaultWidth((FieldUi) uiComponent);
            }
            return FichothequeConstants.LARGE_WIDTH;
        }
        return width;
    }

    public static String getDefaultWidth(FieldUi fieldUi) {
        FieldKey fieldKey = fieldUi.getFieldKey();
        switch (fieldKey.getCategory()) {
            case FieldKey.PROP_CATEGORY:
                return FichothequeConstants.MEDIUM_WIDTH;
            case FieldKey.SPECIAL_CATEGORY: {
                switch (fieldKey.getKeyString()) {
                    case FieldKey.SPECIAL_TITLE:
                    case FieldKey.SPECIAL_SUBTITLE:
                        return FichothequeConstants.LARGE_WIDTH;
                    default:
                        return FichothequeConstants.SMALL_WIDTH;
                }
            }
            default:
                return FichothequeConstants.LARGE_WIDTH;
        }
    }

    public static int getRows(UiComponent uiComponent) {
        return checkRows(uiComponent, -1);
    }

    public static int checkRows(UiComponent uiComponent, int redimLength) {
        int rows = -1;
        String rowsString = uiComponent.getOptionValue(BdfServerConstants.INPUTROWS_OPTION);
        if (!rowsString.isEmpty()) {
            try {
                rows = Integer.parseInt(rowsString);
            } catch (NumberFormatException nfe) {

            }
        }
        if (rows < 1) {
            rows = 1;
            if (uiComponent instanceof FieldUi) {
                if (((FieldUi) uiComponent).getFieldKey().isSection()) {
                    rows = 8;
                }
            }
        }
        if (rows > 1) {
            if (redimLength > (rows * 140)) {
                rows = rows + 2;
            }
            return rows;
        }
        if (redimLength > 80) {
            return 3;
        }
        return 1;
    }

    public static Attribute getIdalphaStyle(SubsetIncludeUi includeUi, Thesaurus thesaurus) {
        Attribute idalphaStyle = includeUi.getAttributes().getAttribute(BdfSpace.IDALPHASTYLE_KEY);
        if (idalphaStyle == null) {
            return thesaurus.getThesaurusMetadata().getAttributes().getAttribute(BdfSpace.IDALPHASTYLE_KEY);
        } else {
            return idalphaStyle;
        }
    }

    public static String getDefVal(FicheFormParameters ficheFormParameters, IncludeUi includeUi) {
        String defautValue = includeUi.getOptionValue(BdfServerConstants.DEFAULTVALUE_OPTION);
        return replaceGenericParameters(ficheFormParameters, defautValue);
    }

    public static String getDefVal(FichePointeur fichePointeur, FicheFormParameters ficheFormParameters, FieldUi fieldUi) {
        if (useDefaultRedacteur(fieldUi)) {
            Redacteur redacteur = ficheFormParameters.getDefaultRedacteur();
            if (redacteur == null) {
                return "";
            }
            String value = redacteur.getLogin();
            SeparatorOptions separatorOptions = ficheFormParameters.getInlineSeparatorOptions();
            if (separatorOptions.isLastInclude()) {
                value = value + separatorOptions.getSeparator();
                if (separatorOptions.isSpaceInclude()) {
                    value = value + " ";
                }
            }
            return value;
        } else {
            FieldKey fieldKey = fieldUi.getFieldKey();
            String defaultValue = fieldUi.getOptionValue(BdfServerConstants.DEFAULTVALUE_OPTION);
            defaultValue = replaceGenericParameters(ficheFormParameters, defaultValue);
            if (defaultValue.length() > 0) {
                if (fieldKey.isInfo()) {
                    CorpusField corpusField = getCorpusField(fichePointeur, fieldUi);
                    if ((corpusField == null) || (!corpusField.isBlockDisplayInfoField())) {
                        SeparatorOptions separatorOptions = ficheFormParameters.getInlineSeparatorOptions();
                        if (separatorOptions.isLastInclude()) {
                            defaultValue = defaultValue + separatorOptions.getSeparator();
                            if (separatorOptions.isSpaceInclude()) {
                                defaultValue = defaultValue + " ";
                            }
                        }
                    }
                }
                return defaultValue;
            }
            return "";
        }
    }

    private static boolean useDefaultRedacteur(FieldUi fieldUi) {
        switch (fieldUi.getFieldString()) {
            case FieldKey.SPECIAL_OWNERS:
                return true;
            default:
                return false;
        }
    }

    public static String replaceGenericParameters(FicheFormParameters ficheFormParameters, String s) {
        int length = s.length();
        if (length < 9) {
            return s;
        }
        int idx1 = s.indexOf("$PARAM_");
        if (idx1 == -1) {
            return s;
        }
        StringBuilder buf = new StringBuilder();
        buf.append(s.substring(0, idx1));
        while (true) {
            int idx2 = s.indexOf('$', idx1 + 1);
            if (idx2 == -1) {
                buf.append(s.substring(idx1));
                break;
            }
            String name = s.substring(idx1 + 7, idx2);
            String val = ficheFormParameters.getSupplementaryParameter(name);
            if (val != null) {
                buf.append(val);
            } else {
                buf.append("??PARAM_").append(name).append("??");
            }
            idx1 = s.indexOf("$PARAM_", idx2 + 1);
            if (idx1 == -1) {
                buf.append(s.substring(idx2 + 1));
                break;
            } else {
                buf.append(s.substring(idx2 + 1, idx1));
            }
        }
        return buf.toString();
    }

    public static boolean hasAccess(FicheFormParameters ficheFormParameters, SubsetIncludeUi includeUi) {
        PermissionSummary permissionSummary = ficheFormParameters.getPermissionSummary();
        if (!permissionSummary.hasAccess(includeUi.getSubsetKey())) {
            return false;
        }
        if (!RoleSpace.canWrite(permissionSummary, includeUi.getAttributes())) {
            return false;
        }
        return true;
    }

    public static boolean hasAccess(FicheFormParameters ficheFormParameters, FieldUi fieldUi) {
        PermissionSummary permissionSummary = ficheFormParameters.getPermissionSummary();
        if (!RoleSpace.canWrite(permissionSummary, fieldUi.getAttributes())) {
            return false;
        }
        return true;
    }

    public static boolean hasAccess(FicheFormParameters ficheFormParameters, SpecialIncludeUi specialIncludeUi) {
        PermissionSummary permissionSummary = ficheFormParameters.getPermissionSummary();
        if (!RoleSpace.canWrite(permissionSummary, specialIncludeUi.getAttributes())) {
            return false;
        }
        return true;
    }

    public static CorpusField getCorpusField(FichePointeur fichePointeur, FieldUi fieldUi) {
        return fichePointeur.getCorpus().getCorpusMetadata().getCorpusField(fieldUi.getFieldKey());
    }

    public static FicheFormParametersBuilder initFicheFormParametersBuilder(OutputParameters outputParameters) {
        return initFicheFormParametersBuilder(outputParameters, outputParameters.getRequestMap());
    }

    public static FicheFormParametersBuilder initFicheFormParametersBuilder(BdfParameters bdfParameters, RequestMap requestMap) {
        boolean withSpecialParseChars;
        if (requestMap.isTrue("brackets")) {
            withSpecialParseChars = false;
        } else {
            withSpecialParseChars = true;
        }
        return FicheFormParametersBuilder.build(bdfParameters)
                .populateFromRequest(requestMap)
                .setWithSpecialParseChars(withSpecialParseChars);
    }

    public static SubsetItemValue toSubsetItemValue(String value, int weight, SubsetItem subsetItem) {
        return new InternalSubsetItemValue(value, weight, subsetItem);
    }

    public static SubsetItemState toSubsetItemState(SubsetItem subsetItem, boolean toAdd) {
        return new InternalSubsetItemState(subsetItem, toAdd);
    }

    public static List<SubsetItemValue> wrap(SubsetItemValue[] array) {
        return new SubsetItemValueList(array);
    }

    public static List<SubsetItemState> wrap(SubsetItemState[] array) {
        return new SubsetItemStateList(array);
    }


    private static class InternalSubsetItemState implements SubsetItemState {

        private final SubsetItem subsetItem;
        private final boolean toAdd;

        private InternalSubsetItemState(SubsetItem subsetItem, boolean toAdd) {
            this.subsetItem = subsetItem;
            this.toAdd = toAdd;
        }

        @Override
        public SubsetItem getSubsetItem() {
            return subsetItem;
        }

        @Override
        public boolean isToAdd() {
            return toAdd;
        }


    }


    private static class InternalSubsetItemValue implements SubsetItemValue {

        private final SubsetItem subsetItem;
        private final int weight;
        private final String value;

        private InternalSubsetItemValue(String value, int weight, SubsetItem subsetItem) {
            this.subsetItem = subsetItem;
            this.weight = weight;
            this.value = value;
        }

        @Override
        public SubsetItem getSubsetItem() {
            return subsetItem;
        }

        @Override
        public int getWeight() {
            return weight;
        }

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

    }


    private static class SubsetItemValueList extends AbstractList<SubsetItemValue> implements RandomAccess {

        private final SubsetItemValue[] array;

        private SubsetItemValueList(SubsetItemValue[] array) {
            this.array = array;
        }

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

        @Override
        public SubsetItemValue get(int index) {
            return array[index];
        }

    }


    private static class SubsetItemStateList extends AbstractList<SubsetItemState> implements RandomAccess {

        private final SubsetItemState[] array;

        private SubsetItemStateList(SubsetItemState[] array) {
            this.array = array;
        }

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

        @Override
        public SubsetItemState get(int index) {
            return array[index];
        }

    }

}
