/* FichothequeLib_Tools - Copyright (c) 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.List;
import java.util.Locale;
import java.util.Set;
import net.fichotheque.FichothequeQuestioner;
import net.fichotheque.SubsetKey;
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.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.sphere.UserLoginException;
import net.fichotheque.utils.FicheUtils;
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.date.FuzzyDate;
import net.mapeadores.util.localisation.CodeCatalog;
import net.mapeadores.util.localisation.Country;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.localisation.LangPreference;
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.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.text.StringUtils;
import net.mapeadores.util.text.TypoOptions;


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

    private FicheItemParser() {

    }

    public static AmountItem parseAmount(String s) throws ParseException {
        String[] result = MoneyUtils.splitMoney(s);
        if (result == null) {
            throw new ParseException("Unable to split money", 0);
        }
        return parseAmount(result[0], result[1]);
    }

    public static AmountItem parseAmount(String valString, String curString) throws ParseException {
        try {
            Decimal decimal = StringUtils.parseDecimal(valString);
            ExtendedCurrency currency = ExtendedCurrency.parse(curString);
            return new AmountItem(decimal, currency);
        } catch (ParseException | NumberFormatException e) {
            throw new ParseException(e.getLocalizedMessage(), 0);
        }
    }

    public static CountryItem parseCountry(String s, CodeCatalog codeCatalog, LangPreference langPreference) throws ParseException {
        try {
            Country country = Country.parse(s);
            return new CountryItem(country);
        } catch (ParseException pe) {
            TextTest textTest = ConditionsUtils.parseTextTest(s);
            if (textTest != null) {
                TextCondition condition = TextConditionBuilder.build(textTest);
                Set<String> result = codeCatalog.getCountryCodeSet(condition, langPreference);
                if (!result.isEmpty()) {
                    for (String code : result) {
                        return new CountryItem(Country.build(code));
                    }
                }
            }
            throw pe;
        }
    }

    public static DateItem parseDate(String s, Locale locale) throws ParseException {
        FuzzyDate date = FuzzyDate.parse(s, locale);
        return new DateItem(date);
    }

    public static EmailItem parseEmail(String s) throws ParseException {
        EmailCore emailCore = EmailCoreUtils.parse(s);
        return new EmailItem(emailCore);
    }

    public static GeopointItem parseGeopoint(String s) throws ParseException {
        int idxAster = s.indexOf('*');
        if (idxAster == -1) {
            throw new ParseException("Missing * character", 0);
        }
        DegreDecimal latitude = parseDegreDecimal(s.substring(0, idxAster));
        DegreDecimal longitude = parseDegreDecimal(s.substring(idxAster + 1));
        return new GeopointItem(latitude, longitude);
    }

    public static ImageItem parseImage(String s, TypoOptions typoOptions) {
        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));
    }

    public static Item parseItem(String s, TypoOptions typoOptions) {
        return new Item(TypoParser.parseTypo(s, typoOptions));
    }

    public static LanguageItem parseLanguage(String s, CodeCatalog codeCatalog, LangPreference langPreference) throws ParseException {
        try {
            Lang lang = Lang.parse(s);
            return new LanguageItem(lang);
        } catch (ParseException pe) {
            TextTest textTest = ConditionsUtils.parseTextTest(s);
            if (textTest != null) {
                TextCondition condition = TextConditionBuilder.build(textTest);
                Set<String> result = codeCatalog.getLangCodeSet(condition, langPreference);
                if (!result.isEmpty()) {
                    for (String code : result) {
                        return new LanguageItem(Lang.build(code));
                    }
                }
            }
            throw pe;
        }
    }

    public static LinkItem parseLink(String s, TypoOptions typoOptions) throws ParseException {
        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) {
            throw new ParseException(e.getLocalizedMessage(), 0);
        }
    }

    public static NumberItem parseNumber(String s) throws ParseException {
        try {
            Decimal decimal = StringUtils.parseDecimal(s);
            return new NumberItem(decimal);
        } catch (NumberFormatException nfe) {
            throw new ParseException(nfe.getLocalizedMessage(), 0);
        }
    }

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

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

    public static FicheItem parsePerson(String s, FichothequeQuestioner fichothequeQuestioner, SubsetKey defaultSphereKey, TypoOptions typoOptions) {
        try {
            return new PersonItem(fichothequeQuestioner.getUserGlobalId(s, defaultSphereKey));
        } catch (UserLoginException ee) {
            try {
                return parsePerson(s, typoOptions);
            } catch (ParseException pe) {
                return parseItem(s, typoOptions);
            }
        }
    }

    public static PersonItem parsePerson(String s, TypoOptions typoOptions) throws ParseException {
        int separateurinterne = '*';
        int posvirg = s.indexOf(separateurinterne);
        if (posvirg == -1) {
            separateurinterne = '|';
            posvirg = s.indexOf(separateurinterne);
        }
        if (posvirg == -1) {
            throw new ParseException("Missing separator", 0);
        }
        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));
    }

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

}
