/* FichothequeLib_Tools - Copyright (c) 2006-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.parsers;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.fichotheque.Fichotheque;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.fiche.AmountItem;
import net.fichotheque.corpus.fiche.CountryItem;
import net.fichotheque.corpus.fiche.DateItem;
import net.fichotheque.corpus.fiche.EmailItem;
import net.fichotheque.corpus.fiche.FicheBlock;
import net.fichotheque.corpus.fiche.FicheBlocks;
import net.fichotheque.corpus.fiche.FicheItem;
import net.fichotheque.corpus.fiche.FicheItems;
import net.fichotheque.corpus.fiche.GeopointItem;
import net.fichotheque.corpus.fiche.ImageItem;
import net.fichotheque.corpus.fiche.Item;
import net.fichotheque.corpus.fiche.LanguageItem;
import net.fichotheque.corpus.fiche.LinkItem;
import net.fichotheque.corpus.fiche.NumberItem;
import net.fichotheque.corpus.fiche.ParaItem;
import net.fichotheque.corpus.fiche.PersonItem;
import net.fichotheque.corpus.metadata.AmountSubfieldKey;
import net.fichotheque.corpus.metadata.CorpusField;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.corpus.metadata.SubfieldKey;
import net.fichotheque.sphere.Redacteur;
import net.fichotheque.tools.corpus.FicheChangeBuilder;
import net.fichotheque.tools.parsers.ficheblock.FicheBlockParser;
import net.fichotheque.utils.FicheUtils;
import net.fichotheque.utils.SphereUtils;
import net.mapeadores.util.conditions.ConditionsUtils;
import net.mapeadores.util.conditions.TextCondition;
import net.mapeadores.util.conditions.TextConditionBuilder;
import net.mapeadores.util.conditions.TextTest;
import net.mapeadores.util.html.HtmlCleaner;
import net.mapeadores.util.localisation.CodeCatalog;
import net.mapeadores.util.localisation.Country;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.localisation.UserLangContext;
import net.mapeadores.util.models.EmailCore;
import net.mapeadores.util.models.EmailCoreUtils;
import net.mapeadores.util.models.PersonCore;
import net.mapeadores.util.models.PersonCoreUtils;
import net.mapeadores.util.money.Currencies;
import net.mapeadores.util.money.ExtendedCurrency;
import net.mapeadores.util.money.MoneyUtils;
import net.mapeadores.util.primitives.Decimal;
import net.mapeadores.util.primitives.DegreDecimal;
import net.mapeadores.util.primitives.DegreSexagesimal;
import net.mapeadores.util.primitives.FuzzyDate;
import net.mapeadores.util.text.CleanedString;
import net.mapeadores.util.text.DateFormatBundle;
import net.mapeadores.util.text.StringUtils;
import net.mapeadores.util.text.TypoOptions;


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

    private final Parameters parameters;
    private final Fichotheque fichotheque;
    private final FicheChangeBuilder ficheChangeBuilder;
    private final FicheBlockParser ficheBlockParser;
    private final DateFormatBundle dateFormatBundle;
    private final TypoOptions typoOptions;
    private final TextContentParser textContentParser;
    private final Map<FieldKey, SubfieldBuffer> subfieldBufferMap = new HashMap<FieldKey, SubfieldBuffer>();
    private SubsetKey defaultSphereKey = null;

    public FicheParser(Parameters parameters, Corpus corpus, FicheChangeBuilder ficheChangeBuilder) {
        this.parameters = parameters;
        this.ficheChangeBuilder = ficheChangeBuilder;
        this.dateFormatBundle = DateFormatBundle.getDateFormatBundle(parameters.getUserLangContext().getFormatLocale());
        Redacteur userRedacteur = parameters.getUserRedacteur();
        if (userRedacteur != null) {
            this.defaultSphereKey = userRedacteur.getSubsetKey();
        }
        this.typoOptions = parameters.getTypoOptions();
        fichotheque = corpus.getFichotheque();
        textContentParser = new TextContentParser(typoOptions);
        ficheBlockParser = new FicheBlockParser(parameters.getHtmlCleaner(), typoOptions, false);
    }

    public void flushParsedSubfields() {
        for (SubfieldBuffer subfieldBuffer : subfieldBufferMap.values()) {
            subfieldBuffer.flush();
        }
    }

    public void parseCorpusField(CorpusField corpusField, String value) {
        switch (corpusField.getCategory()) {
            case FieldKey.PROP_CATEGORY:
                parseProp(corpusField, cleanPropToken(value, parameters.getItemListSeparateur()));
                break;
            case FieldKey.INFO_CATEGORY:
                String[] tokens = StringUtils.getTechnicalTokens(value, parameters.getItemListSeparateur(), false);
                parseInfo(corpusField, tokens);
                break;
            case FieldKey.SECTION_CATEGORY:
                parseSection(corpusField, value);
                break;
            case FieldKey.SPECIAL_CATEGORY:
                switch (corpusField.getFieldString()) {
                    case FieldKey.SPECIAL_TITLE:
                        parseTitle(value);
                        break;
                    case FieldKey.SPECIAL_SUBTITLE:
                        parseSubtitle(value);
                        break;
                    case FieldKey.SPECIAL_LANG:
                        parseFicheLang(value);
                        break;
                    case FieldKey.SPECIAL_OWNERS: {
                        String[] ownerTokens = StringUtils.getTechnicalTokens(value, parameters.getItemListSeparateur(), false);
                        parseOwners(ownerTokens);
                        break;
                    }
                }
                break;
        }
    }

    public void parseSubfield(CorpusField corpusField, SubfieldKey subfieldKey, String value) {
        if (SubfieldKey.isLegalSubfield(corpusField, subfieldKey.getSubtype())) {
            SubfieldBuffer fieldBuffer = subfieldBufferMap.get(corpusField.getFieldKey());
            if (fieldBuffer == null) {
                fieldBuffer = new SubfieldBuffer(corpusField);
                subfieldBufferMap.put(corpusField.getFieldKey(), fieldBuffer);
            }
            fieldBuffer.putSubfield(subfieldKey, value);
        }
    }

    public void parseFicheLang(String s) {
        if (s == null) {
            return;
        }
        LanguageItem langue = parseLanguage(s);
        if (langue != null) {
            ficheChangeBuilder.setLang(langue.getLang());
        } else {
            ficheChangeBuilder.setLang(null);
        }
    }

    public void parseTitle(String s) {
        ficheChangeBuilder.setTitle(CleanedString.newInstance(TypoParser.parseTypo(s, typoOptions)));
    }

    public void parseSubtitle(String s) {
        CleanedString cs = CleanedString.newInstance(s);
        if (cs != null) {
            ficheChangeBuilder.setSubtitle(parsePara(cs.toString()));
        } else {
            ficheChangeBuilder.setSubtitle(null);
        }
    }

    public void parseOwners(String[] tokens) {
        FicheItems ficheItems = parsePersonList(tokens, fichotheque, defaultSphereKey, typoOptions);
        ficheChangeBuilder.putOwners(ficheItems);
    }

    public void parseProp(CorpusField propField, String s) {
        s = StringUtils.cleanString(s);
        FicheItem ficheItem = null;
        if (s.length() > 0) {
            ficheItem = parseFicheItem(s, propField);
        }
        ficheChangeBuilder.setProp(propField.getFieldKey(), ficheItem);
    }

    public void parsePersonProp(CorpusField propField, String surname, String forename, String nonlatin, String surnameFirstString) {
        surname = StringUtils.cleanString(surname);
        forename = StringUtils.cleanString(forename);
        nonlatin = StringUtils.cleanString(nonlatin);
        boolean surnameFirst = StringUtils.isTrue(surnameFirstString);
        FicheItem redacteurItem = FicheParser.this.parsePerson(surname, forename, nonlatin, surnameFirst);
        ficheChangeBuilder.setProp(propField.getFieldKey(), redacteurItem);
    }

    public void parseAmountProp(CorpusField propField, String value, String cur) {
        FicheItem ficheItem = null;
        boolean itemAllowed = true;
        value = StringUtils.cleanString(value);
        if (value.length() > 0) {
            ficheItem = FicheParser.this.parseAmount(value);
            if (ficheItem == null) {
                cur = StringUtils.cleanString(cur);
                if (cur.length() == 0) {
                    Currencies currencies = propField.getCurrencies();
                    cur = currencies.get(0).getCurrencyCode();
                }
                ficheItem = parseAmount(value, cur);
            }
            if (ficheItem == null) {
                if (itemAllowed) {
                    if (!value.startsWith("?")) {
                        value = "?" + value;
                    }
                }
                ficheItem = parseItem(value, itemAllowed);
            }
        }
        ficheChangeBuilder.setProp(propField.getFieldKey(), ficheItem);
    }

    public void parseImageProp(CorpusField propField, String src, String alt, String title) {
        ImageItem image = null;
        src = StringUtils.cleanString(src);
        if (src.length() > 0) {
            alt = StringUtils.cleanString(alt);
            title = StringUtils.cleanString(title);
            image = new ImageItem(src, alt, title);
        }
        ficheChangeBuilder.setProp(propField.getFieldKey(), image);
    }

    public void parseGeopointProp(CorpusField propField, String latitude, String longitude) {
        latitude = StringUtils.cleanString(latitude);
        longitude = StringUtils.cleanString(longitude);
        DegreDecimal latitudeDecimal = parseDegreDecimal(latitude);
        DegreDecimal longitudeDecimal = parseDegreDecimal(longitude);
        FicheItem ficheItem = null;
        boolean itemAllowed = true;
        if ((latitudeDecimal != null) && (longitudeDecimal != null)) {
            ficheItem = new GeopointItem(latitudeDecimal, longitudeDecimal);
        } else {
            if ((latitude.length() > 0) || (longitude.length() > 0)) {
                if (itemAllowed) {
                    if (!latitude.startsWith("?? ")) {
                        latitude = "?? " + latitude;
                    }
                }
                ficheItem = parseItem(latitude + " ?? " + longitude, itemAllowed);
            }
        }
        ficheChangeBuilder.setProp(propField.getFieldKey(), ficheItem);
    }

    public void parseInfo(CorpusField infoField, String[] tokens) {
        List<FicheItem> ficheItemCollection = new ArrayList<FicheItem>();
        for (String token : tokens) {
            FicheItem ficheItem = parseFicheItem(token, infoField);
            if (ficheItem != null) {
                ficheItemCollection.add(ficheItem);
            }
        }
        addInInfo(infoField, FicheUtils.toFicheItems(ficheItemCollection));
    }

    public void addInInfo(CorpusField infoField, FicheItems ficheItems) {
        ficheChangeBuilder.putInfo(infoField.getFieldKey(), ficheItems);
    }

    public void parseSection(CorpusField sectionField, String s) {
        List<FicheBlock> list = new ArrayList<FicheBlock>();
        ficheBlockParser.parseFicheBlockList(s, list);
        FicheBlocks ficheBlocks = FicheUtils.toFicheBlocks(list);
        ficheChangeBuilder.putSection(sectionField.getFieldKey(), ficheBlocks);
    }

    public FicheItem parseFicheItem(String token, CorpusField corpusField) {
        boolean itemAllowed = true;
        switch (corpusField.getFicheItemType()) {
            case CorpusField.LANGUAGE_FIELD: {
                LanguageItem languageItem = parseLanguage(token);
                if (languageItem != null) {
                    return languageItem;
                } else {
                    return parseItem(token, itemAllowed);
                }
            }
            case CorpusField.COUNTRY_FIELD:
                CountryItem countryItem = parseCountry(token);
                if (countryItem != null) {
                    return countryItem;
                } else {
                    return parseItem(token, itemAllowed);
                }

            case CorpusField.DATE_FIELD: {
                try {
                    FuzzyDate date = FuzzyDate.parse(token, dateFormatBundle);
                    return new DateItem(date);
                } catch (ParseException pe) {
                    return parseItem(token, itemAllowed);
                }
            }
            case CorpusField.LINK_FIELD: {
                LinkItem link = parseLink(token);
                if (link != null) {
                    return link;
                } else {
                    return parseItem(token, itemAllowed);
                }
            }
            case CorpusField.NUMBER_FIELD: {
                try {
                    Decimal decimal = StringUtils.parseDecimal(token);
                    return new NumberItem(decimal);
                } catch (NumberFormatException nfe) {
                    if (itemAllowed) {
                        if (!token.startsWith("?")) {
                            token = "?" + token;
                        }
                    }
                    return parseItem(token, itemAllowed);
                }
            }
            case CorpusField.AMOUNT_FIELD: {
                AmountItem amountItem = FicheParser.this.parseAmount(token);
                if (amountItem != null) {
                    return amountItem;
                }
                if (itemAllowed) {
                    if (!token.startsWith("?")) {
                        token = "?" + token;
                    }
                }
                return parseItem(token, itemAllowed);
            }
            case CorpusField.EMAIL_FIELD: {
                try {
                    EmailCore emailCore = EmailCoreUtils.parse(token);
                    return new EmailItem(emailCore);
                } catch (ParseException pe) {
                    return parseItem(token, itemAllowed);
                }
            }
            case CorpusField.PERSON_FIELD: {
                return parsePerson(token, fichotheque, getDefaultSphereKey(corpusField), typoOptions);
            }
            case CorpusField.ITEM_FIELD: {
                return new Item(TypoParser.parseTypo(token, typoOptions));
            }
            case CorpusField.GEOPOINT_FIELD: {
                GeopointItem geopoint = parseGeopoint(token);
                if (geopoint != null) {
                    return geopoint;
                } else {
                    return parseItem(token, itemAllowed);
                }
            }
            case CorpusField.PARA_FIELD: {
                return parsePara(token);
            }
            case CorpusField.IMAGE_FIELD: {
                ImageItem image = parseImage(token);
                if (image != null) {
                    return image;
                } else {
                    return parseItem(token, itemAllowed);
                }
            }
            default:
                return null;
        }
    }

    private ParaItem parsePara(String token) {
        ParaItem.Builder paraBuilder = new ParaItem.Builder();
        textContentParser.parse(paraBuilder, token);
        return paraBuilder.toParaItem();
    }

    private FicheItem parsePerson(String surname, String forename, String nonlatin, boolean surnameFirst) {
        if ((forename.length() == 0) && (nonlatin.length() == 0)) {
            try {
                Redacteur redacteur = SphereUtils.parse(fichotheque, surname);
                return new PersonItem(redacteur.getGlobalId());
            } catch (SphereUtils.RedacteurLoginException rle) {
            }
            if (surname.length() == 0) {
                return null;
            }
            if (!surnameFirst) {
                return new Item(TypoParser.parseTypo(surname, typoOptions));
            }
        }
        surname = TypoParser.parseTypo(surname, typoOptions);
        forename = TypoParser.parseTypo(forename, typoOptions);
        nonlatin = TypoParser.parseTypo(nonlatin, typoOptions);
        return new PersonItem(PersonCoreUtils.toPersonCore(surname, forename, nonlatin, surnameFirst), null);
    }

    public FicheItem parseAmountInfoSubfield(CorpusField corpusField, ExtendedCurrency currency, String value) {
        value = StringUtils.cleanString(value);
        boolean itemAllowed = true;
        if (value.isEmpty()) {
            return null;
        }
        try {
            Decimal decimal = StringUtils.parseDecimal(value);
            return new AmountItem(decimal, currency);
        } catch (NumberFormatException nfe) {
            if (itemAllowed) {
                if (!value.startsWith("?")) {
                    value = "?" + value;
                }
            }
            return parseItem(value, itemAllowed);
        }
    }

    private Item parseItem(String token, boolean itemAllowed) {
        if (itemAllowed) {
            return new Item(TypoParser.parseTypo(token, typoOptions));
        } else {
            return null;
        }
    }

    public LinkItem parseLink(String s) {
        int separateurinterne = '*';
        int posseparateur = s.indexOf(separateurinterne);
        if (posseparateur == -1) {
            separateurinterne = '|';
            posseparateur = s.indexOf(separateurinterne);
        }
        String href;
        if (posseparateur == - 1) {
            href = s;
        } else {
            href = s.substring(0, posseparateur).trim();
        }
        if (href.startsWith("www.")) {
            href = "http://" + href;
        }
        try {
            URI uri = new URI(href);
            if (uri.isAbsolute()) {
                URL url = uri.toURL();
                href = url.toString();
            } else {
                href = uri.toString();
            }
            String title = "";
            String comment = "";
            if (posseparateur > -1) {
                String bout = s.substring(posseparateur + 1).trim();
                int pos2 = bout.indexOf(separateurinterne);
                if (pos2 > -1) {
                    title = bout.substring(0, pos2).trim();
                    comment = bout.substring(pos2 + 1).trim();
                } else {
                    title = bout;
                }
            }
            return new LinkItem(href, TypoParser.parseTypo(title, typoOptions), TypoParser.parseTypo(comment, typoOptions));
        } catch (URISyntaxException | MalformedURLException e) {
            return null;
        }
    }

    public ImageItem parseImage(String s) {
        int separateurinterne = '*';
        int posseparateur = s.indexOf(separateurinterne);
        String src;
        String alt = "";
        String title = "";
        if (posseparateur == - 1) {
            src = s;
        } else {
            src = s.substring(0, posseparateur).trim();
            int pos2 = s.indexOf(separateurinterne, posseparateur + 1);
            if (pos2 == -1) {
                alt = s.substring(posseparateur + 1).trim();
            } else {
                alt = s.substring(posseparateur + 1, pos2).trim();
                title = s.substring(pos2 + 1).trim();
            }
        }
        return new ImageItem(src, TypoParser.parseTypo(alt, typoOptions), TypoParser.parseTypo(title, typoOptions));
    }

    private GeopointItem parseGeopoint(String s) {
        DegreDecimal latitude = null;
        DegreDecimal longitude = null;
        int idxAster = s.indexOf('*');
        if (idxAster != -1) {
            latitude = parseDegreDecimal(s.substring(0, idxAster));
            longitude = parseDegreDecimal(s.substring(idxAster + 1));
        } else {
        }
        if ((latitude != null) && (longitude != null)) {
            return new GeopointItem(latitude, longitude);
        } else {
            return null;
        }
    }

    private DegreDecimal parseDegreDecimal(String s) {
        try {
            Decimal decimal = StringUtils.parseDecimal(s);
            return DegreDecimal.newInstance(decimal);
        } catch (NumberFormatException nfe) {
            try {
                DegreSexagesimal ds = DegreSexagesimal.parse(s);
                return DegreDecimal.newInstance(ds);
            } catch (ParseException pe) {
                return null;
            }
        }
    }

    private AmountItem parseAmount(String s) {
        String[] result = MoneyUtils.splitMoney(s);
        if (result == null) {
            return null;
        }
        return parseAmount(result[0], result[1]);
    }


    private AmountItem parseAmount(String valString, String curString) {
        try {
            Decimal decimal = StringUtils.parseDecimal(valString);
            ExtendedCurrency currency = ExtendedCurrency.parse(curString);
            return new AmountItem(decimal, currency);
        } catch (ParseException | NumberFormatException e) {
            return null;
        }
    }

    private LanguageItem parseLanguage(String valString) {
        try {
            Lang lang = Lang.parse(valString);
            return new LanguageItem(lang);
        } catch (ParseException mcle) {
            TextTest textTest = ConditionsUtils.parseTextTest(valString);
            if (textTest != null) {
                TextCondition condition = TextConditionBuilder.build(textTest);
                Set<String> result = parameters.getCodeCatalog().getLangCodeSet(condition, parameters.getUserLangContext().getLangPreference());
                if (!result.isEmpty()) {
                    for (String code : result) {
                        return new LanguageItem(Lang.build(code));
                    }
                }
            }
            return null;
        }
    }

    private CountryItem parseCountry(String valString) {
        try {
            Country country = Country.parse(valString);
            return new CountryItem(country);
        } catch (ParseException pe) {
            TextTest textTest = ConditionsUtils.parseTextTest(valString);
            if (textTest != null) {
                TextCondition condition = TextConditionBuilder.build(textTest);
                Set<String> result = parameters.getCodeCatalog().getCountryCodeSet(condition, parameters.getUserLangContext().getLangPreference());
                if (!result.isEmpty()) {
                    for (String code : result) {
                        return new CountryItem(Country.build(code));
                    }
                }
            }
            return null;
        }
    }


    private SubsetKey getDefaultSphereKey(CorpusField corpusField) {
        SubsetKey customSphereKey = corpusField.getDefaultSphereKey();
        if (customSphereKey == null) {
            customSphereKey = defaultSphereKey;
        }
        return customSphereKey;
    }

    private String cleanPropToken(String s, char itemListSeparateur) {
        int length = s.length();
        int p = length;
        for (int i = (length - 1); i >= 0; i--) {
            char carac = s.charAt(i);
            if ((carac == itemListSeparateur) || (Character.isWhitespace(carac))) {
                p--;
            } else {
                break;
            }
        }
        if (p == 0) {
            return "";
        } else if (p == length) {
            return s;
        } else {
            return s.substring(0, p);
        }
    }

    public static FicheItems parsePersonList(String[] tokens, Fichotheque fichotheque, SubsetKey defaultSphereKey, TypoOptions typoOptions) {
        List<FicheItem> list = new ArrayList<FicheItem>();
        for (String token : tokens) {
            FicheItem redacteurItem = parsePerson(token, fichotheque, defaultSphereKey, typoOptions);
            if (redacteurItem != null) {
                list.add(redacteurItem);
            }
        }
        return FicheUtils.toFicheItems(list);
    }

    public static FicheItem parsePerson(String token, Fichotheque fichotheque, SubsetKey defaultSphereKey, TypoOptions typoOptions) {
        try {
            Redacteur redacteur = SphereUtils.parse(fichotheque, token, defaultSphereKey);
            return new PersonItem(redacteur.getGlobalId());
        } catch (SphereUtils.RedacteurLoginException rle) {
            PersonItem personItem = FicheParser.parsePerson(token, typoOptions);
            if (personItem != null) {
                return personItem;
            }
            return new Item(TypoParser.parseTypo(token, typoOptions));
        }
    }

    public static PersonItem parsePerson(String s, TypoOptions typoOptions) {
        int separateurinterne = '*';
        int posvirg = s.indexOf(separateurinterne);
        if (posvirg == -1) {
            separateurinterne = '|';
            posvirg = s.indexOf(separateurinterne);
        }
        if (posvirg > -1) {
            String surname = TypoParser.parseTypo(s, 0, posvirg, typoOptions);
            String forename = "";
            String organism = null;
            if (posvirg < (s.length() - 1)) {
                String suite = s.substring(posvirg + 1).trim();
                int pos2 = suite.indexOf(separateurinterne);
                if (pos2 > -1) {
                    forename = suite.substring(0, pos2);
                    if (pos2 < (suite.length() - 1)) {
                        organism = suite.substring(pos2 + 1);
                    }
                } else {
                    forename = suite;
                }
            }
            boolean surnameFirst = false;
            if ((forename.length() > 0) && (forename.charAt(0) == '%')) {
                forename = forename.substring(1);
                surnameFirst = true;
            }
            forename = TypoParser.parseTypo(forename, typoOptions);
            PersonCore personCore = PersonCoreUtils.toPersonCore(surname, forename, "", surnameFirst);
            return new PersonItem(personCore, TypoParser.parseTypo(organism, typoOptions));
        } else {
            return null;
        }
    }


    private class SubfieldBuffer {

        private final CorpusField corpusField;
        private final Map<SubfieldKey, String> subfieldMap = new LinkedHashMap<SubfieldKey, String>();

        private SubfieldBuffer(CorpusField corpusField) {
            this.corpusField = corpusField;
        }

        private void putSubfield(SubfieldKey subfieldKey, String value) {
            subfieldMap.put(subfieldKey, value);
        }

        private void flush() {
            switch (corpusField.getFicheItemType()) {
                case CorpusField.AMOUNT_FIELD:
                    if (corpusField.isProp()) {
                        flushAmountProp();
                    } else {
                        flushAmountInfo();
                    }
                    break;
                case CorpusField.PERSON_FIELD:
                    flushPerson();
                    break;
                case CorpusField.GEOPOINT_FIELD:
                    flushGeopoint();
                    break;
                case CorpusField.IMAGE_FIELD:
                    flushImage();
                    break;
            }
        }

        private void flushAmountProp() {
            String num = "";
            String cur = "";
            for (Map.Entry<SubfieldKey, String> entry : subfieldMap.entrySet()) {
                String value = entry.getValue();
                switch (entry.getKey().getSubtype()) {
                    case SubfieldKey.CURRENCY_SUBTYPE:
                        cur = value;
                        break;
                    case SubfieldKey.VALUE_SUBTYPE:
                        num = value;
                        break;
                }
            }
            parseAmountProp(corpusField, num, cur);
        }

        private void flushAmountInfo() {
            Set<ExtendedCurrency> currencySet = new HashSet<ExtendedCurrency>();
            List<FicheItem> ficheItemCollection = new ArrayList<FicheItem>();
            String othersValue = null;
            for (Map.Entry<SubfieldKey, String> entry : subfieldMap.entrySet()) {
                String value = entry.getValue();
                SubfieldKey subfieldKey = entry.getKey();
                switch (entry.getKey().getSubtype()) {
                    case SubfieldKey.AMOUNT_SUBTYPE:
                        FicheItem ficheItem = parseAmountInfoSubfield(corpusField, ((AmountSubfieldKey) subfieldKey).getCurrency(), value);
                        if (ficheItem != null) {
                            if (ficheItem instanceof AmountItem) {
                                AmountItem amountItem = (AmountItem) ficheItem;
                                if (!currencySet.contains(amountItem.getCurrency())) {
                                    ficheItemCollection.add(amountItem);
                                    currencySet.add(amountItem.getCurrency());
                                }
                            } else {
                                ficheItemCollection.add(ficheItem);
                            }
                        }
                        break;
                    case SubfieldKey.OTHERS_SUBTYPE:
                        othersValue = value;
                        break;

                }
            }
            if (othersValue != null) {
                String[] tokens = StringUtils.getTechnicalTokens(othersValue, parameters.getItemListSeparateur(), false);
                int length = tokens.length;
                for (int i = 0; i < length; i++) {
                    FicheItem ficheItem = parseFicheItem(tokens[i], corpusField);
                    if (ficheItem != null) {
                        if (ficheItem instanceof AmountItem) {
                            AmountItem amountItem = (AmountItem) ficheItem;
                            if (!currencySet.contains(amountItem.getCurrency())) {
                                ficheItemCollection.add(amountItem);
                                currencySet.add(amountItem.getCurrency());
                            }
                        } else {
                            ficheItemCollection.add(ficheItem);
                        }
                    }
                }
            }
            addInInfo(corpusField, FicheUtils.toFicheItems(ficheItemCollection));
        }

        private void flushGeopoint() {
            String latitude = "";
            String longitude = "";
            for (Map.Entry<SubfieldKey, String> entry : subfieldMap.entrySet()) {
                String value = entry.getValue();
                switch (entry.getKey().getSubtype()) {
                    case SubfieldKey.LATITUDE_SUBTYPE:
                        latitude = value;
                        break;
                    case SubfieldKey.LONGITUDE_SUBTYPE:
                        longitude = value;
                        break;
                }
            }
            parseGeopointProp(corpusField, latitude, longitude);
        }

        private void flushPerson() {
            String surname = "";
            String forename = "";
            String nonlatin = "";
            String surnameFirstString = "";
            for (Map.Entry<SubfieldKey, String> entry : subfieldMap.entrySet()) {
                String value = entry.getValue();
                switch (entry.getKey().getSubtype()) {
                    case SubfieldKey.SURNAME_SUBTYPE:
                        surname = value;
                        break;
                    case SubfieldKey.FORENAME_SUBTYPE:
                        forename = value;
                        break;
                    case SubfieldKey.NONLATIN_SUBTYPE:
                        nonlatin = value;
                        break;
                    case SubfieldKey.SURNAMEFIRST_SUBTYPE:
                        surnameFirstString = value;
                        break;
                }
            }
            parsePersonProp(corpusField, surname, forename, nonlatin, surnameFirstString);
        }

        private void flushImage() {
            String src = "";
            String alt = "";
            String title = "";
            for (Map.Entry<SubfieldKey, String> entry : subfieldMap.entrySet()) {
                String value = entry.getValue();
                switch (entry.getKey().getSubtype()) {
                    case SubfieldKey.SRC_SUBTYPE:
                        src = value;
                        break;
                    case SubfieldKey.ALT_SUBTYPE:
                        alt = value;
                        break;
                    case SubfieldKey.TITLE_SUBTYPE:
                        title = value;
                        break;
                }
            }
            parseImageProp(corpusField, src, alt, title);
        }

    }


    public static class Parameters {

        private final UserLangContext userLangContext;
        private final CodeCatalog codeCatalog;
        private final HtmlCleaner htmlCleaner;
        private final TypoOptions typoOptions;
        private char itemListSeparateur = ';';
        private Redacteur userRedacteur = null;

        public Parameters(UserLangContext userLangContext, CodeCatalog codeCatalog, HtmlCleaner htmlCleaner, TypoOptions typoOptions) {
            this.userLangContext = userLangContext;
            this.codeCatalog = codeCatalog;
            this.htmlCleaner = htmlCleaner;
            this.typoOptions = typoOptions;
        }

        public Lang getWorkingLang() {
            return userLangContext.getWorkingLang();
        }

        public UserLangContext getUserLangContext() {
            return userLangContext;
        }

        public CodeCatalog getCodeCatalog() {
            return codeCatalog;
        }

        public HtmlCleaner getHtmlCleaner() {
            return htmlCleaner;
        }

        public TypoOptions getTypoOptions() {
            return typoOptions;
        }

        public char getItemListSeparateur() {
            return itemListSeparateur;
        }

        public void setItemListSeparateur(char itemListSeparateur) {
            this.itemListSeparateur = itemListSeparateur;
        }

        public Redacteur getUserRedacteur() {
            return userRedacteur;
        }

        public void setUserRedacteur(Redacteur userRedacteur) {
            this.userRedacteur = userRedacteur;
        }

    }

}
