/* FichothequeLib_API - Copyright (c) 2016-2022 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 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.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.syntax.FormSyntax;
import net.fichotheque.utils.SphereUtils;
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.primitives.FuzzyDate;
import net.mapeadores.util.text.DateFormatBundle;
import net.mapeadores.util.text.StringUtils;


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

    private FicheItemJson() {

    }

    public static void object(JSONWriter jsonWriter, FicheItem ficheItem, PropertyEligibility propertyEligibility, LangContext langContext, FichothequeQuestioner fichothequeQuestioner, MessageLocalisationProvider messageLocalisationProvider) throws IOException {
        jsonWriter.object();
        properties(jsonWriter, ficheItem, propertyEligibility, langContext, fichothequeQuestioner, messageLocalisationProvider);
        jsonWriter.endObject();

    }

    public static void properties(JSONWriter jsonWriter, FicheItem ficheItem, PropertyEligibility propertyEligibility, LangContext langContext, FichothequeQuestioner fichothequeQuestioner, MessageLocalisationProvider messageLocalisationProvider) throws IOException {
        if (ficheItem instanceof Item) {
            properties(jsonWriter, (Item) ficheItem, propertyEligibility);
        } else if (ficheItem instanceof DateItem) {
            properties(jsonWriter, (DateItem) ficheItem, propertyEligibility, langContext);
        } else if (ficheItem instanceof CountryItem) {
            properties(jsonWriter, (CountryItem) ficheItem, propertyEligibility, langContext, messageLocalisationProvider);
        } else if (ficheItem instanceof LanguageItem) {
            properties(jsonWriter, (LanguageItem) ficheItem, propertyEligibility, langContext, messageLocalisationProvider);
        } else if (ficheItem instanceof GeopointItem) {
            properties(jsonWriter, (GeopointItem) ficheItem, propertyEligibility, langContext, messageLocalisationProvider);
        } else if (ficheItem instanceof LinkItem) {
            properties(jsonWriter, (LinkItem) ficheItem, propertyEligibility);
        } else if (ficheItem instanceof ImageItem) {
            properties(jsonWriter, (ImageItem) ficheItem, propertyEligibility);
        } else if (ficheItem instanceof EmailItem) {
            properties(jsonWriter, (EmailItem) ficheItem, propertyEligibility);
        } else if (ficheItem instanceof NumberItem) {
            properties(jsonWriter, (NumberItem) ficheItem, propertyEligibility, langContext);
        } else if (ficheItem instanceof AmountItem) {
            properties(jsonWriter, (AmountItem) ficheItem, propertyEligibility, langContext);
        } else if (ficheItem instanceof ParaItem) {
            properties(jsonWriter, (ParaItem) ficheItem, propertyEligibility);
        } else if (ficheItem instanceof PersonItem) {
            properties(jsonWriter, (PersonItem) ficheItem, propertyEligibility, fichothequeQuestioner);
        }
        if (propertyEligibility.includeProperty("formsyntax")) {
            jsonWriter.key("formsyntax");
            jsonWriter.value(FormSyntax.toString(ficheItem, fichothequeQuestioner, null));
        }
    }

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

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

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

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

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

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

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

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

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

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

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

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

    public static void dateLabelsObject(JSONWriter jsonWriter, FuzzyDate date, LangContext langContext) throws IOException {
        jsonWriter.object();
        if (langContext instanceof ListLangContext) {
            for (ListLangContext.Unit unit : (ListLangContext) langContext) {
                DateFormatBundle dateFormatBundle = DateFormatBundle.getDateFormatBundle(unit.getFormatLocale());
                jsonWriter.key(unit.getLang().toString());
                jsonWriter.value(date.getDateLitteral(dateFormatBundle));
            }
        } else if (langContext instanceof UserLangContext) {
            UserLangContext userLangContext = (UserLangContext) langContext;
            DateFormatBundle dateFormatBundle = DateFormatBundle.getDateFormatBundle(userLangContext.getFormatLocale());
            jsonWriter.key(userLangContext.getWorkingLang().toString());
            jsonWriter.value(date.getDateLitteral(dateFormatBundle));
        }
        jsonWriter.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 jsonWriter, String code, LangContext langContext, MessageLocalisationProvider messageLocalisationProvider) throws IOException {
        jsonWriter.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);
                    jsonWriter.key(currentLang.toString());
                    jsonWriter.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);
                jsonWriter.key(userLangContext.getWorkingLang().toString());
                jsonWriter.value(message);
            }
        }
        jsonWriter.endObject();
    }

    public static void geoLabelsObject(JSONWriter jsonWriter, 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);
        }
        jsonWriter.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) {
                    jsonWriter.key(lang.toString());
                    jsonWriter.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) {
                jsonWriter.key(userLangContext.getWorkingLang().toString());
                jsonWriter.value(value + message);
            }
        }
        jsonWriter.endObject();
    }

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

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

}
