/* BdfServer - Copyright (c) 2009-2024 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.FormElement;
import fr.exemole.bdfserver.api.ficheform.ThesaurusIncludeElement;
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 fr.exemole.bdfserver.tools.ficheform.builders.ChoiceThesaurusIncludeElementBuilder;
import fr.exemole.bdfserver.tools.ficheform.builders.FicheStyleThesaurusIncludeElementBuilder;
import fr.exemole.bdfserver.tools.ficheform.builders.HiddenFieldElementBuilder;
import fr.exemole.bdfserver.tools.ficheform.builders.HiddenThesaurusIncludeElementBuilder;
import fr.exemole.bdfserver.tools.ficheform.builders.ItemFieldElementBuilder;
import fr.exemole.bdfserver.tools.ficheform.builders.NotEditableThesaurusIncludeElementBuilder;
import fr.exemole.bdfserver.tools.ficheform.builders.TextFieldElementBuilder;
import fr.exemole.bdfserver.tools.ficheform.builders.TextThesaurusIncludeElementBuilder;
import java.util.Collection;
import net.fichotheque.Subset;
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.croisement.Liaison;
import net.fichotheque.include.ExtendedIncludeKey;
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.annotation.Nullable;
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 FormElement.Field toFormElement(FichePointeur fichePointeur, FicheFormParameters ficheFormParameters, CorpusField corpusField, FieldUi fieldUi) {
        String defaultValue = ficheFormParameters.getCustomDefaultValue(fieldUi.getName());
        return toFormElement(fichePointeur, ficheFormParameters, corpusField, fieldUi, defaultValue);
    }

    public static FormElement.Field toFormElement(FichePointeur fichePointeur, FicheFormParameters ficheFormParameters, CorpusField corpusField, FieldUi fieldUi, @Nullable String defaultValue) {
        if (corpusField.isSection()) {
            TextFieldElementBuilder builder = TextFieldElementBuilder.check(fichePointeur, ficheFormParameters, corpusField, fieldUi, defaultValue);
            if (builder != null) {
                return builder.toTextFieldElement();
            } else {
                return null;
            }
        }
        switch (fieldUi.getOptionValue(BdfServerConstants.INPUTTYPE_OPTION)) {
            case BdfServerConstants.INPUT_HIDDEN: {
                HiddenFieldElementBuilder builder = HiddenFieldElementBuilder.check(fichePointeur, ficheFormParameters, corpusField, fieldUi, defaultValue);
                if (builder != null) {
                    return builder.toHiddenFieldElement();
                } else {
                    return null;
                }
            }
            default: {
                ItemFieldElementBuilder builder = ItemFieldElementBuilder.check(fichePointeur, ficheFormParameters, corpusField, fieldUi, defaultValue);
                if (builder != null) {
                    return builder.toItemFieldElement();
                } else {
                    return null;
                }
            }
        }
    }

    public static FormElement.Include toThesaurusIncludeElement(FichePointeur fichePointeur, FicheFormParameters ficheFormParameters, SubsetIncludeUi includeUi) {
        String inputType = includeUi.getOptionValue(BdfServerConstants.INPUTTYPE_OPTION, BdfServerConstants.INPUT_TEXT);
        switch (inputType) {
            case BdfServerConstants.INPUT_CHECK: {
                ChoiceThesaurusIncludeElementBuilder builder = ChoiceThesaurusIncludeElementBuilder.check(fichePointeur, ficheFormParameters, includeUi);
                if (builder != null) {
                    return builder.toListThesaurusIncludeElement();
                } else {
                    return null;
                }
            }
            case BdfServerConstants.INPUT_RADIO:
            case BdfServerConstants.INPUT_SELECT: {
                ChoiceThesaurusIncludeElementBuilder builder = ChoiceThesaurusIncludeElementBuilder.check(fichePointeur, ficheFormParameters, includeUi);
                if (builder != null) {
                    ThesaurusIncludeElement.Choice ltie = builder.toListThesaurusIncludeElement();
                    if (ltie.getEntryList().size() > 1) {
                        TextThesaurusIncludeElementBuilder ttieb2 = TextThesaurusIncludeElementBuilder.check(fichePointeur, ficheFormParameters, includeUi);
                        if (ttieb2 != null) {
                            return ttieb2.toTextThesaurusIncludeElement();
                        } else {
                            return null;
                        }
                    } else {
                        return ltie;
                    }
                } else {
                    return null;
                }
            }
            case BdfServerConstants.INPUT_HIDDEN: {
                HiddenThesaurusIncludeElementBuilder builder = HiddenThesaurusIncludeElementBuilder.check(fichePointeur, ficheFormParameters, includeUi);
                if (builder != null) {
                    return builder.toHiddenIncludeElement();
                } else {
                    return null;
                }
            }
            case BdfServerConstants.INPUT_NOTEDITABLE: {
                NotEditableThesaurusIncludeElementBuilder builder = NotEditableThesaurusIncludeElementBuilder.check(fichePointeur, ficheFormParameters, includeUi);
                if (builder != null) {
                    return builder.toNotEditableIndexationElement();
                } else {
                    return null;
                }
            }
            case BdfServerConstants.INPUT_FICHESTYLE: {
                FicheStyleThesaurusIncludeElementBuilder builder = FicheStyleThesaurusIncludeElementBuilder.check(fichePointeur, ficheFormParameters, includeUi);
                if (builder != null) {
                    return builder.toFicheStyleThesaurusIncludeElement();
                } else {
                    return null;
                }
            }
            case BdfServerConstants.INPUT_TEXT:
            default: {
                TextThesaurusIncludeElementBuilder builder = TextThesaurusIncludeElementBuilder.check(fichePointeur, ficheFormParameters, includeUi);
                if (builder != null) {
                    return builder.toTextThesaurusIncludeElement();
                } else {
                    return null;
                }
            }
        }
    }

    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, fichePointeur.getFichotheque(), getParameters(ficheFormParameters, corpusField));
        }
        if (obj instanceof FicheItems) {
            boolean isBlock = corpusField.isBlockDisplayInfoField();
            SeparatorOptions separatorOptions = (isBlock) ? ficheFormParameters.getBlockSeparatorOptions() : ficheFormParameters.getInlineSeparatorOptions();
            return FormSyntax.toString((FicheItems) obj, fichePointeur.getFichotheque(), 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.getLien().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 = BdfServerConstants.checkWidth(width);
            } catch (IllegalArgumentException iae) {
                width = "";
            }
        }
        if (width.isEmpty()) {
            if (uiComponent instanceof FieldUi) {
                return getDefaultWidth((FieldUi) uiComponent);
            }
            return BdfServerConstants.WIDTH_LARGE;
        }
        return width;
    }

    private static String getDefaultWidth(FieldUi fieldUi) {
        FieldKey fieldKey = fieldUi.getFieldKey();
        switch (fieldKey.getCategory()) {
            case FieldKey.PROP_CATEGORY:
                return BdfServerConstants.WIDTH_MEDIUM;
            case FieldKey.SPECIAL_CATEGORY: {
                switch (fieldKey.getKeyString()) {
                    case FieldKey.SPECIAL_TITLE:
                    case FieldKey.SPECIAL_SUBTITLE:
                        return BdfServerConstants.WIDTH_LARGE;
                    default:
                        return BdfServerConstants.WIDTH_SMALL;
                }
            }
            default:
                return BdfServerConstants.WIDTH_LARGE;
        }
    }

    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 FicheFormParameters toFicheFormParameters(OutputParameters outputParameters) {
        return initFicheFormParametersBuilder(outputParameters)
                .toFicheFormParameters();
    }

    public static FicheFormParameters toFicheFormParameters(BdfParameters bdfParameters, RequestMap requestMap) {
        return initFicheFormParametersBuilder(bdfParameters, requestMap)
                .toFicheFormParameters();
    }

}
