/* FichothequeLib_API - Copyright (c) 2016-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.json;

import java.io.IOException;
import java.text.DecimalFormatSymbols;
import java.text.ParseException;
import java.time.format.FormatStyle;
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.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.Redacteur;
import net.fichotheque.utils.SphereUtils;
import net.mapeadores.util.date.FuzzyDate;
import net.mapeadores.util.date.FuzzyDateFormatter;
import net.mapeadores.util.json.JSONWriter;
import net.mapeadores.util.json.PropertyEligibility;
import net.mapeadores.util.localisation.Country;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.localisation.LangContext;
import net.mapeadores.util.localisation.ListLangContext;
import net.mapeadores.util.localisation.MessageLocalisation;
import net.mapeadores.util.localisation.MessageLocalisationProvider;
import net.mapeadores.util.localisation.SpecialCodes;
import net.mapeadores.util.localisation.UserLangContext;
import net.mapeadores.util.models.EmailCore;
import net.mapeadores.util.models.PersonCore;
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;


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

    private FicheItemJson() {

    }

    public static void properties(JSONWriter jw, PersonItem personItem, PropertyEligibility propertyEligibility, FichothequeQuestioner fichothequeQuestioner) throws IOException {
        String userGlobalId = personItem.getRedacteurGlobalId();
        if (propertyEligibility.includeProperty("type")) {
            jw.key("type")
                    .value("person");
        }
        if (propertyEligibility.includeProperty("code")) {
            jw.key("code");
            if (userGlobalId != null) {
                Redacteur redacteur = fichothequeQuestioner.getRedacteurByGlobalId(userGlobalId);
                if (redacteur != null) {
                    jw.value(redacteur.getBracketStyle());
                } else {
                    jw.value(userGlobalId);
                }
            } else {
                jw.value(personItem.getPersonCore().toStandardStyle());
            }
        }
        if (propertyEligibility.includeProperty("sphere")) {
            jw.key("sphere");
            if (userGlobalId != null) {
                try {
                    SubsetKey sphereKey = SphereUtils.getSubsetKey(userGlobalId);
                    jw.value(sphereKey.getSubsetName());
                } catch (ParseException pe) {
                    jw.value("#ERR: wrong redacteurGlobaleId = " + userGlobalId);
                }
            } else {
                jw.value("");
            }
        }
        if (propertyEligibility.includeProperty("login")) {
            jw.key("login");
            if (userGlobalId != null) {
                Redacteur redacteur = fichothequeQuestioner.getRedacteurByGlobalId(userGlobalId);
                if (redacteur != null) {
                    jw.value(redacteur.getLogin());
                } else {
                    jw.value("");
                }
            } else {
                jw.value("");
            }
        }
        PersonCore personCore = fichothequeQuestioner.toPersonCore(personItem);
        if (propertyEligibility.includeProperty("standard")) {
            jw.key("standard")
                    .value(personCore.toStandardStyle());
        }
        if (propertyEligibility.includeProperty("directory")) {
            jw.key("directory")
                    .value(personCore.toDirectoryStyle(false));
        }
        if (propertyEligibility.includeProperty("updirectory")) {
            jw.key("updirectory")
                    .value(personCore.toDirectoryStyle(true));
        }
        if (propertyEligibility.includeProperty("biblio")) {
            jw.key("biblio")
                    .value(personCore.toBiblioStyle(false));
        }
        if (propertyEligibility.includeProperty("upbiblio")) {
            jw.key("upbiblio")
                    .value(personCore.toBiblioStyle(true));
        }
        if (propertyEligibility.includeProperty("surname")) {
            jw.key("surname")
                    .value(personCore.getSurname());
        }
        if (propertyEligibility.includeProperty("upsurname")) {
            jw.key("upsurname")
                    .value(personCore.getSurname().toUpperCase());
        }
        if (propertyEligibility.includeProperty("forename")) {
            jw.key("forename")
                    .value(personCore.getForename());
        }
        if (propertyEligibility.includeProperty("nonlatin")) {
            jw.key("nonlatin")
                    .value(personCore.getNonlatin());
        }
        if (propertyEligibility.includeProperty("surnamefirst")) {
            jw.key("surnamefirst")
                    .value(personCore.isSurnameFirst());
        }
        if (propertyEligibility.includeProperty("organism")) {
            jw.key("organism")
                    .value(personItem.getOrganism());
        }
    }

    public static void properties(JSONWriter jw, DateItem dateItem, PropertyEligibility propertyEligibility, LangContext langContext, FormatStyle formatStyle) throws IOException {
        FuzzyDate date = dateItem.getDate();
        if (propertyEligibility.includeProperty("type")) {
            jw.key("type")
                    .value("date");
        }
        if (propertyEligibility.includeProperty("datetype")) {
            jw.key("datetype")
                    .value(date.getDateType());
        }
        if (propertyEligibility.includeProperty("code")) {
            jw.key("code")
                    .value(date.toString());
        }
        if (propertyEligibility.includeProperty("iso")) {
            jw.key("iso")
                    .value(date.toISOString(false));
        }
        if (propertyEligibility.includeProperty("lastday")) {
            jw.key("lastday")
                    .value(date.toISOString(true));
        }
        if (propertyEligibility.includeProperty("year")) {
            jw.key("year")
                    .value(date.getYear());
        }
        if (propertyEligibility.includeProperty("isomonth")) {
            jw.key("isomonth")
                    .value(date.toMonthString(false));
        }
        if (propertyEligibility.includeProperty("lastmonth")) {
            jw.key("lastmonth")
                    .value(date.toMonthString(true));
        }
        if (propertyEligibility.includeProperty("labels")) {
            jw.key("labels");
            dateLabelsObject(jw, date, langContext, formatStyle);
        }
        if (propertyEligibility.includeProperty("monthlabels")) {
            jw.key("monthlabels");
            dateLabelsObject(jw, date.truncate(FuzzyDate.MONTH_TYPE), langContext, formatStyle);
        }
    }

    public static void properties(JSONWriter jw, CountryItem countryItem, PropertyEligibility propertyEligibility, LangContext langContext, MessageLocalisationProvider messageLocalisationProvider) throws IOException {
        if (propertyEligibility.includeProperty("type")) {
            jw.key("type")
                    .value("country");
        }
        if (propertyEligibility.includeProperty("code")) {
            jw.key("code")
                    .value(countryItem.getCountry().toString());
        }
        if (propertyEligibility.includeProperty("labels")) {
            jw.key("labels");
            countryLabelsObject(jw, countryItem.getCountry(), langContext, messageLocalisationProvider);
        }
    }

    public static void properties(JSONWriter jw, LanguageItem languageItem, PropertyEligibility propertyEligibility, LangContext langContext, MessageLocalisationProvider messageLocalisationProvider) throws IOException {
        if (propertyEligibility.includeProperty("type")) {
            jw.key("type")
                    .value("lang");
        }
        if (propertyEligibility.includeProperty("code")) {
            jw.key("code")
                    .value(languageItem.getLang().toString());
        }
        if (propertyEligibility.includeProperty("labels")) {
            jw.key("labels");
            langLabelsObject(jw, languageItem.getLang(), langContext, messageLocalisationProvider);
        }
    }

    public static void properties(JSONWriter jw, GeopointItem geopointItem, PropertyEligibility propertyEligibility, LangContext langContext, MessageLocalisationProvider messageLocalisationProvider) throws IOException {
        if (propertyEligibility.includeProperty("type")) {
            jw.key("type")
                    .value("geopoint");
        }
        if (propertyEligibility.includeProperty("lat")) {
            jw.key("lat")
                    .value(geopointItem.getLatitude().toString());
        }
        if (propertyEligibility.includeProperty("lon")) {
            jw.key("lon")
                    .value(geopointItem.getLongitude().toString());
        }
        if (propertyEligibility.includeProperty("latlabels")) {
            jw.key("latlabels");
            geoLabelsObject(jw, geopointItem.getLatitude(), true, langContext, messageLocalisationProvider);
        }
        if (propertyEligibility.includeProperty("lonlabels")) {
            jw.key("lonlabels");
            geoLabelsObject(jw, geopointItem.getLongitude(), false, langContext, messageLocalisationProvider);
        }
    }

    public static void properties(JSONWriter jw, LinkItem linkItem, PropertyEligibility propertyEligibility) throws IOException {
        if (propertyEligibility.includeProperty("type")) {
            jw.key("type")
                    .value("link");
        }
        if (propertyEligibility.includeProperty("href")) {
            jw.key("href")
                    .value(linkItem.getHref());
        }
        if (propertyEligibility.includeProperty("title")) {
            jw.key("title")
                    .value(linkItem.getTitle());
        }
        if (propertyEligibility.includeProperty("comment")) {
            jw.key("comment")
                    .value(linkItem.getComment());
        }
    }

    public static void properties(JSONWriter jw, ImageItem imageItem, PropertyEligibility propertyEligibility) throws IOException {
        if (propertyEligibility.includeProperty("type")) {
            jw.key("type")
                    .value("image");
        }
        if (propertyEligibility.includeProperty("src")) {
            jw.key("src")
                    .value(imageItem.getSrc());
        }
        if (propertyEligibility.includeProperty("title")) {
            jw.key("title")
                    .value(imageItem.getTitle());
        }
        if (propertyEligibility.includeProperty("alt")) {
            jw.key("alt")
                    .value(imageItem.getAlt());
        }
    }

    public static void properties(JSONWriter jw, EmailItem emailItem, PropertyEligibility propertyEligibility) throws IOException {
        EmailCore emailCore = emailItem.getEmailCore();
        if (propertyEligibility.includeProperty("type")) {
            jw.key("type")
                    .value("email");
        }
        if (propertyEligibility.includeProperty("complete")) {
            jw.key("complete")
                    .value(emailCore.toCompleteString());
        }
        if (propertyEligibility.includeProperty("address")) {
            jw.key("address")
                    .value(emailCore.getAddrSpec());
        }
        if (propertyEligibility.includeProperty("name")) {
            jw.key("name")
                    .value(emailCore.getRealName());
        }
    }

    public static void properties(JSONWriter jw, NumberItem numberItem, PropertyEligibility propertyEligibility, LangContext langContext) throws IOException {
        if (propertyEligibility.includeProperty("type")) {
            jw.key("type")
                    .value("number");
        }
        if (propertyEligibility.includeProperty("code")) {
            jw.key("code")
                    .value(numberItem.getDecimal().toString());
        }
        if (propertyEligibility.includeProperty("value")) {
            jw.key("value")
                    .value(numberItem.getDecimal());
        }
        if (propertyEligibility.includeProperty("labels")) {
            jw.key("labels");
            decimalLabelsObject(jw, numberItem.getDecimal(), langContext, "");
        }
    }

    public static void properties(JSONWriter jw, AmountItem amountItem, PropertyEligibility propertyEligibility, LangContext langContext) throws IOException {
        if (propertyEligibility.includeProperty("type")) {
            jw.key("type")
                    .value("amount");
        }
        if (propertyEligibility.includeProperty("code")) {
            jw.key("code")
                    .value(amountItem.toString());
        }
        if (propertyEligibility.includeProperty("currency")) {
            jw.key("currency")
                    .value(amountItem.getCurrency().getCurrencyCode());
        }
        if (propertyEligibility.includeProperty("decimal")) {
            jw.key("decimal")
                    .value(amountItem.getDecimal().toString());
        }
        if (propertyEligibility.includeProperty("labels")) {
            jw.key("labels");
            amountLabelsObject(jw, amountItem.getDecimal(), amountItem.getCurrency(), langContext);
        }
        if (propertyEligibility.includeProperty("long")) {
            jw.key("long")
                    .value(amountItem.toMoneyLong());
        }
    }

    public static void properties(JSONWriter jw, ParaItem paraItem, PropertyEligibility propertyEligibility) throws IOException {
        if (propertyEligibility.includeProperty("type")) {
            jw.key("type")
                    .value("para");
        }
        if (propertyEligibility.includeProperty("raw")) {
            jw.key("para")
                    .value(paraItem.contentToString());
        }
    }

    public static void properties(JSONWriter jw, Item item, PropertyEligibility propertyEligibility) throws IOException {
        if (propertyEligibility.includeProperty("type")) {
            jw.key("type")
                    .value("item");
        }
        if (propertyEligibility.includeProperty("value")) {
            jw.key("value")
                    .value(item.getValue());
        }
    }

    public static void dateLabelsObject(JSONWriter jw, FuzzyDate date, LangContext langContext, FormatStyle formatStyle) throws IOException {
        jw.object();
        if (langContext instanceof ListLangContext) {
            for (ListLangContext.Unit unit : (ListLangContext) langContext) {
                FuzzyDateFormatter fuzzyDateFormatter = FuzzyDateFormatter.build(formatStyle, unit.getFormatLocale());
                jw.key(unit.getLang().toString())
                        .value(fuzzyDateFormatter.format(date));
            }
        } else if (langContext instanceof UserLangContext) {
            UserLangContext userLangContext = (UserLangContext) langContext;
            FuzzyDateFormatter fuzzyDateFormatter = FuzzyDateFormatter.build(formatStyle, userLangContext.getFormatLocale());
            jw.key(userLangContext.getWorkingLang().toString())
                    .value(fuzzyDateFormatter.format(date));
        }
        jw.endObject();
    }

    public static void countryLabelsObject(JSONWriter jsonWriter, Country country, LangContext langContext, MessageLocalisationProvider messageLocalisationProvider) throws IOException {
        localisationLabelsObject(jsonWriter, country.toString(), langContext, messageLocalisationProvider);
    }

    public static void langLabelsObject(JSONWriter jsonWriter, Lang lang, LangContext langContext, MessageLocalisationProvider messageLocalisationProvider) throws IOException {
        localisationLabelsObject(jsonWriter, lang.toString(), langContext, messageLocalisationProvider);
    }

    private static void localisationLabelsObject(JSONWriter jw, String code, LangContext langContext, MessageLocalisationProvider messageLocalisationProvider) throws IOException {
        jw.object();
        if (langContext instanceof ListLangContext) {
            for (ListLangContext.Unit unit : (ListLangContext) langContext) {
                Lang currentLang = unit.getLang();
                MessageLocalisation messageLocalisation = messageLocalisationProvider.getMessageLocalisation(currentLang);
                String message = messageLocalisation.toString(code);
                if (message != null) {
                    message = StringUtils.getFirstPart(message);
                    jw.key(currentLang.toString())
                            .value(message);
                }
            }
        } else if (langContext instanceof UserLangContext) {
            UserLangContext userLangContext = (UserLangContext) langContext;
            MessageLocalisation messageLocalisation = messageLocalisationProvider.getMessageLocalisation(userLangContext);
            String message = messageLocalisation.toString(code);
            if (message != null) {
                message = StringUtils.getFirstPart(message);
                jw.key(userLangContext.getWorkingLang().toString())
                        .value(message);
            }
        }
        jw.endObject();
    }

    public static void geoLabelsObject(JSONWriter jw, DegreDecimal degreDecimal, boolean latitude, LangContext langContext, MessageLocalisationProvider messageLocalisationProvider) throws IOException {
        DegreSexagesimal sexa = DegreSexagesimal.fromDegreDecimal(degreDecimal);
        String value = sexa.toString(false, " ");
        String code;
        if (latitude) {
            code = SpecialCodes.getLatitudeSpecialCode(sexa);
        } else {
            code = SpecialCodes.getLongitudeSpecialCode(sexa);
        }
        jw.object();
        if (langContext instanceof ListLangContext) {
            for (ListLangContext.Unit unit : (ListLangContext) langContext) {
                Lang lang = unit.getLang();
                MessageLocalisation messageLocalisation = messageLocalisationProvider.getMessageLocalisation(lang);
                String message = messageLocalisation.toString(code);
                if (message != null) {
                    jw.key(lang.toString())
                            .value(value + message);
                }
            }
        } else if (langContext instanceof UserLangContext) {
            UserLangContext userLangContext = (UserLangContext) langContext;
            MessageLocalisation messageLocalisation = messageLocalisationProvider.getMessageLocalisation(userLangContext);
            String message = messageLocalisation.toString(code);
            if (message != null) {
                jw.key(userLangContext.getWorkingLang().toString())
                        .value(value + message);
            }
        }
        jw.endObject();
    }

    public static void decimalLabelsObject(JSONWriter jw, Decimal decimal, LangContext langContext, String suffix) throws IOException {
        jw.object();
        if (langContext instanceof ListLangContext) {
            for (ListLangContext.Unit unit : (ListLangContext) langContext) {
                DecimalFormatSymbols symbols = new DecimalFormatSymbols(unit.getFormatLocale());
                String value = decimal.toString(symbols);
                if (!suffix.isEmpty()) {
                    value = value + suffix;
                }
                jw.key(unit.getLang().toString())
                        .value(value);
            }
        } else if (langContext instanceof UserLangContext) {
            UserLangContext userLangContext = (UserLangContext) langContext;
            DecimalFormatSymbols symbols = new DecimalFormatSymbols(userLangContext.getFormatLocale());
            String value = decimal.toString(symbols);
            if (!suffix.isEmpty()) {
                value = value + suffix;
            }
            jw.key(userLangContext.getWorkingLang().toString())
                    .value(value);
        }
        jw.endObject();
    }

    public static void amountLabelsObject(JSONWriter jw, Decimal decimal, ExtendedCurrency currency, LangContext langContext) throws IOException {
        jw.object();
        if (langContext instanceof ListLangContext) {
            for (ListLangContext.Unit unit : (ListLangContext) langContext) {
                DecimalFormatSymbols symbols = new DecimalFormatSymbols(unit.getFormatLocale());
                String value = MoneyUtils.toLitteralString(decimal, currency, symbols);
                jw.key(unit.getLang().toString())
                        .value(value);
            }
        } else if (langContext instanceof UserLangContext) {
            UserLangContext userLangContext = (UserLangContext) langContext;
            DecimalFormatSymbols symbols = new DecimalFormatSymbols(userLangContext.getFormatLocale());
            String value = MoneyUtils.toLitteralString(decimal, currency, symbols);
            jw.key(userLangContext.getWorkingLang().toString())
                    .value(value);
        }
        jw.endObject();
    }

}
