/* 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.format.formatters;

import net.fichotheque.corpus.SubfieldValue;
import net.fichotheque.corpus.fiche.FicheItem;
import net.fichotheque.corpus.metadata.CorpusField;
import net.fichotheque.corpus.metadata.SubfieldKey;
import net.fichotheque.format.FormatContext;
import net.fichotheque.format.FormatSource;
import net.fichotheque.format.formatters.FicheItemFormatter;
import net.fichotheque.format.formatters.SubfieldFormatter;
import net.fichotheque.tools.format.patterndefs.DefaultPattern;
import net.fichotheque.utils.FormatterUtils;
import net.mapeadores.util.exceptions.SwitchException;
import net.mapeadores.util.format.FormatConstants;
import net.mapeadores.util.instruction.Argument;
import net.mapeadores.util.instruction.Instruction;
import net.mapeadores.util.instruction.InstructionResolver;
import net.mapeadores.util.logging.ErrorMessageException;
import net.mapeadores.util.logging.MessageHandler;


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

    private final static SubfieldFormatter STRING_VALUE_PART = (subfieldValue, formatSource) -> {
        Object obj = subfieldValue.getValue();
        if (!(obj instanceof String)) {
            return "#ERR: " + obj.getClass().getName();
        }
        return (String) obj;
    };
    private final static SubfieldFormatter STRING_VALUE_UP_PART = (subfieldValue, formatSource) -> {
        Object obj = subfieldValue.getValue();
        if (!(obj instanceof String)) {
            return "#ERR: " + obj.getClass().getName();
        }
        return FormatterParserUtils.toUppercase((String) obj, formatSource);
    };

    private SubfieldFormatterParser() {

    }

    public static SubfieldFormatter parse(String subfieldType, String pattern, FormatContext formatContext, MessageHandler messageHandler) {
        if (pattern == null) {
            pattern = DefaultPattern.subfield(subfieldType);
        }
        try {
            return parse(subfieldType, pattern, formatContext);
        } catch (ErrorMessageException fe) {
            messageHandler.addMessage(FormatConstants.SEVERE_PATTERN, fe.getErrorMessage());
            return null;
        }
    }

    public static SubfieldFormatter parse(String subfieldType, String pattern, FormatContext formatContext) throws ErrorMessageException {
        Object[] partArray = FormatterUtils.parsePattern(new InternalInstructionResolver(formatContext, subfieldType), pattern);
        return new InternalSubfieldFormatter(partArray);
    }


    private static class InternalSubfieldFormatter implements SubfieldFormatter {

        private final Object[] partArray;

        private InternalSubfieldFormatter(Object[] partArray) {
            this.partArray = partArray;
        }

        @Override
        public String formatSubfield(SubfieldValue subfieldValue, FormatSource formatSource) {
            if (subfieldValue == null) {
                return null;
            }
            StringBuilder buf = new StringBuilder();
            int length = partArray.length;
            for (int i = 0; i < length; i++) {
                Object obj = partArray[i];
                if (obj instanceof String) {
                    buf.append((String) obj);
                    continue;
                }
                SubfieldFormatter formatter = (SubfieldFormatter) obj;
                buf.append(formatter.formatSubfield(subfieldValue, formatSource));
            }
            return buf.toString();
        }

    }


    private static class InternalInstructionResolver implements InstructionResolver {

        private final FormatContext formatContext;
        private final String subfieldType;

        private InternalInstructionResolver(FormatContext formatContext, String subfieldType) {
            this.formatContext = formatContext;
            this.subfieldType = subfieldType;
        }

        @Override
        public Object resolve(Instruction instruction) throws ErrorMessageException {
            InstructionResolver resolver = formatContext.getInstructionResolverProvider().getInstructionResolver(SubfieldFormatter.class);
            if (resolver != null) {
                Object formatter = resolver.resolve(instruction);
                if (formatter != null) {
                    return formatter;
                }
            }
            switch (subfieldType) {
                case SubfieldKey.SURNAME_SUBTYPE:
                case SubfieldKey.FORENAME_SUBTYPE:
                case SubfieldKey.NONLATIN_SUBTYPE:
                case SubfieldKey.SURNAMEFIRST_SUBTYPE:
                case SubfieldKey.SRC_SUBTYPE:
                case SubfieldKey.ALT_SUBTYPE:
                case SubfieldKey.TITLE_SUBTYPE:
                case SubfieldKey.LATITUDE_SUBTYPE:
                case SubfieldKey.LONGITUDE_SUBTYPE:
                case SubfieldKey.CURRENCY_SUBTYPE:
                case SubfieldKey.VALUE_SUBTYPE:
                    return resolveStringValue(instruction);
                case SubfieldKey.OTHERS_SUBTYPE:
                case SubfieldKey.AMOUNT_SUBTYPE:
                    return resolveAmount(instruction, formatContext);
                case SubfieldKey.LANG_SUBTYPE:
                default:
                    throw new SwitchException("subfieldType = " + subfieldType);
            }
        }

        private SubfieldFormatter resolveStringValue(Instruction instruction) {
            Argument arg1 = instruction.get(0);
            String key = arg1.getKey();
            switch (key) {
                case "formsyntax":
                    return STRING_VALUE_PART;
                case "value":
                    boolean up = FormatterParserUtils.isUppercase(instruction);
                    if (up) {
                        return STRING_VALUE_UP_PART;
                    } else {
                        return STRING_VALUE_PART;
                    }
                default:
                    return null;
            }
        }

        private SubfieldFormatter resolveAmount(Instruction instruction, FormatContext formatContext) throws ErrorMessageException {
            FicheItemFormatter ficheItemFormatter = FicheItemFormatterParser.resolveFicheItem(instruction, CorpusField.AMOUNT_FIELD, formatContext, null);
            if (ficheItemFormatter == null) {
                return null;
            }
            return new FicheItemSubfieldPart(ficheItemFormatter);
        }

    }


    private static class FicheItemSubfieldPart implements SubfieldFormatter {

        private final FicheItemFormatter ficheItemFormatter;

        private FicheItemSubfieldPart(FicheItemFormatter ficheItemFormatter) {
            this.ficheItemFormatter = ficheItemFormatter;
        }

        @Override
        public String formatSubfield(SubfieldValue subfieldValue, FormatSource formatSource) {
            Object obj = subfieldValue.getValue();
            if (!(obj instanceof FicheItem)) {
                return "#ERR: " + obj.getClass().getName();
            }
            return ficheItemFormatter.formatFicheItem((FicheItem) obj, formatSource);
        }

    }

}
