/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
#pragma once

#include <svl/svldllapi.h>
#include <com/sun/star/i18n/XNumberFormatCode.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <i18nlangtag/lang.h>
#include <svl/ondemand.hxx>
#include <svl/zforlist.hxx>
#include <unotools/charclass.hxx>

#include <functional>
#include <map>

class Color;
class ImpSvNumberformatScan;
class ImpSvNumberInputScan;
class SvNumberFormatter;

class SVL_DLLPUBLIC SvNFLanguageData
{
public:
    SvNFLanguageData(const css::uno::Reference<css::uno::XComponentContext>& rxContext,
                     LanguageType eLang, const SvNumberFormatter& rColorCallback);
    SvNFLanguageData(const SvNFLanguageData& rOther);
    ~SvNFLanguageData();

    const css::uno::Reference<css::uno::XComponentContext>& GetComponentContext() const
    {
        return xContext;
    }

    // return the corresponding LocaleData wrapper
    const LocaleDataWrapper* GetLocaleData() const;

    // return the corresponding CharacterClassification wrapper
    const CharClass* GetCharClass() const;

    // return the corresponding Calendar wrapper
    CalendarWrapper* GetCalendar() const;

    // return corresponding Transliteration wrapper
    const ::utl::TransliterationWrapper* GetTransliteration() const;

    //! The following method is not to be used from outside but must be
    //! public for the InputScanner.
    // return the current FormatScanner
    const ImpSvNumberformatScan* GetFormatScanner() const;

    // return current (!) Locale
    const LanguageTag& GetLanguageTag() const;

    /// Get compatibility ("automatic" old style) currency from I18N locale data
    void GetCompatibilityCurrency(OUString& rSymbol, OUString& rAbbrev) const;

    // cached locale data items

    // return the corresponding decimal separator
    const OUString& GetNumDecimalSep() const;

    // return the corresponding decimal separator alternative
    const OUString& GetNumDecimalSepAlt() const;

    // return the corresponding group (AKA thousand) separator
    const OUString& GetNumThousandSep() const;

    // return the corresponding date separator
    const OUString& GetDateSep() const;

    // checks for decimal separator and optional alternative
    bool IsDecimalSep(std::u16string_view rStr) const;

    /// Return the decimal separator matching the given locale / LanguageType.
    OUString GetLangDecimalSep(LanguageType nLang);

    /// Change language/country, also input and format scanner
    void ChangeIntl(LanguageType eLnge);

    /** Set evaluation type and order of input date strings
        @see NfEvalDateFormat
     */
    void SetEvalDateFormat(NfEvalDateFormat eEDF) { eEvalDateFormat = eEDF; }
    NfEvalDateFormat GetEvalDateFormat() const { return eEvalDateFormat; }

    struct InputScannerPrivateAccess
    {
        friend class ImpSvNumberInputScan;

    private:
        InputScannerPrivateAccess() {}
    };
    /** Access for input scanner to temporarily (!) switch locales. */
    OnDemandLocaleDataWrapper& GetOnDemandLocaleDataWrapper(const InputScannerPrivateAccess&)
    {
        return xLocaleData;
    }

    /** The language with which the formatter was initialized (system setting),
        NOT the current language after a ChangeIntl() */
    LanguageType GetIniLanguage() const { return IniLnge; }

    LanguageType ImpResolveLanguage(LanguageType eLnge) const;

    /// Return the reference date
    const Date& GetNullDate() const;

    sal_uInt16 GetCurrencyFormatStrings(NfWSStringsDtor&, const NfCurrencyEntry&, bool bBank) const;

    void ChangeStandardPrec(short nPrec);

    sal_uInt16 ExpandTwoDigitYear(sal_uInt16 nYear) const;

private:
    friend class SvNFEngine;
    friend class SvNFFormatData;
    friend class SvNumberFormatter;

    css::uno::Reference<css::uno::XComponentContext> xContext;

    const LanguageType IniLnge; // Initial language/country setting
    LanguageType ActLnge; // Current language/country setting
    LanguageTag aLanguageTag;
    OnDemandCharClass xCharClass; // CharacterClassification
    OnDemandLocaleDataWrapper xLocaleData; // LocaleData switched between SYSTEM, ENGLISH and other
    OnDemandTransliterationWrapper xTransliteration; // Transliteration loaded on demand
    OnDemandCalendarWrapper xCalendar; // Calendar loaded on demand
    std::unique_ptr<ImpSvNumberInputScan> pStringScanner; // Input string scanner
    std::unique_ptr<ImpSvNumberformatScan> pFormatScanner; // Format code string scanner

    // cached locale data items needed almost every time
    OUString aDecimalSep;
    OUString aDecimalSepAlt;
    OUString aThousandSep;
    OUString aDateSep;

    NfEvalDateFormat eEvalDateFormat; // DateFormat evaluation
};

class SVL_DLLPUBLIC SvNFFormatData
{
public:
    typedef std::map<sal_uInt32, sal_uInt32> DefaultFormatKeysMap;
    typedef std::map<sal_uInt32, std::unique_ptr<SvNumberformat>> FormatEntryMap;

private:
    FormatEntryMap aFTable; // Table of format keys to format entries
    DefaultFormatKeysMap aDefaultFormatKeys; // Table of default standard to format keys
    sal_uInt32 MaxCLOffset; // Max language/country offset used
    sal_uInt32 nDefaultSystemCurrencyFormat; // NewCurrency matching SYSTEM locale
    bool bNoZero; // Zero value suppression

public:
    SvNFFormatData();
    ~SvNFFormatData();

public:
    const SvNumberformat* GetFormatEntry(sal_uInt32 nKey) const;

    SvNumFormatType GetType(sal_uInt32 nFIndex) const;

    const FormatEntryMap& GetFormatEntryMap() const { return aFTable; }

    /// Whether format index nFIndex is of type text or not
    bool IsTextFormat(sal_uInt32 nFIndex) const;

    OUString GetCalcCellReturn(sal_uInt32 nFormat) const;

    /** Whether nFormat is of type css::util::NumberFormat::CURRENCY and the format code
        contains a new SYMBOLTYPE_CURRENCY and if so which one [$xxx-nnn].
        If ppEntry is not NULL and exactly one entry is found, a [$xxx-nnn] is
        returned, even if the format code only contains [$xxx] !
     */
    bool GetNewCurrencySymbolString(sal_uInt32 nFormat, OUString& rSymbol,
                                    const NfCurrencyEntry** ppEntry, bool* pBank = nullptr) const;

    void MergeDefaultFormatKeys(const DefaultFormatKeysMap& rDefaultFormatKeys);

private:
    SvNFFormatData(const SvNFFormatData&) = delete;
    SvNFFormatData& operator=(const SvNFFormatData&) = delete;

    friend class SvNFEngine;
    friend class SvNumberFormatter;

    // Obtain the format entry for a given key index.
    SVL_DLLPRIVATE SvNumberformat* GetFormatEntry(sal_uInt32 nKey);

    SVL_DLLPRIVATE SvNumberformat* GetEntry(sal_uInt32 nKey) const;

    /// Return whether zero suppression is switched on
    SVL_DLLPRIVATE bool GetNoZero() const { return bNoZero; }
    SVL_DLLPRIVATE void SetNoZero(bool bNZ) { bNoZero = bNZ; }

    SVL_DLLPRIVATE sal_uInt32 ImpIsEntry(std::u16string_view rString, sal_uInt32 nCLOffset,
                                         LanguageType eLnge) const;

    // Return CLOffset or (MaxCLOffset + SV_COUNTRY_LANGUAGE_OFFSET) if new language/country
    SVL_DLLPRIVATE sal_uInt32 ImpGetCLOffset(LanguageType eLnge) const;

    // Generate builtin formats provided by i18n behind CLOffset,
    // if bNoAdditionalFormats==false also generate additional i18n formats.
    SVL_DLLPRIVATE void ImpGenerateFormats(SvNFLanguageData& rCurrentLanguage,
                                           const NativeNumberWrapper& rNatNum, sal_uInt32 CLOffset,
                                           bool bNoAdditionalFormats);

    // Create builtin formats for language/country if necessary, return CLOffset
    SVL_DLLPRIVATE sal_uInt32 ImpGenerateCL(SvNFLanguageData& rCurrentLanguage,
                                            const NativeNumberWrapper& rNatNum, LanguageType eLnge);

    // Generate additional formats provided by i18n
    SVL_DLLPRIVATE void ImpGenerateAdditionalFormats(
        SvNFLanguageData& rCurrentLanguage, const NativeNumberWrapper& rNatNum, sal_uInt32 CLOffset,
        css::uno::Reference<css::i18n::XNumberFormatCode> const& rNumberFormatCode,
        bool bAfterChangingSystemCL);

    // called by SvNumberFormatterRegistry_Impl::Notify if the default system currency changes
    SVL_DLLPRIVATE void ResetDefaultSystemCurrency();

    SVL_DLLPRIVATE bool PutEntry(SvNFLanguageData& rCurrentLanguage,
                                 const NativeNumberWrapper& rNatNum, OUString& rString,
                                 sal_Int32& nCheckPos, SvNumFormatType& nType, sal_uInt32& nKey,
                                 LanguageType eLnge = LANGUAGE_DONTKNOW,
                                 bool bReplaceBooleanEquivalent = true);

    SVL_DLLPRIVATE SvNumberformat*
    ImpInsertFormat(SvNFLanguageData& rCurrentLanguage, const NativeNumberWrapper& rNatNum,
                    const css::i18n::NumberFormatCode& rCode, sal_uInt32 nPos,
                    bool bAfterChangingSystemCL = false, sal_Int16 nOrgIndex = 0);

    // Return the format index of the currency format of the current locale.
    // Format is created if not already present.
    SVL_DLLPRIVATE sal_uInt32 ImpGetDefaultCurrencyFormat(SvNFLanguageData& rCurrentLanguage,
                                                          const NativeNumberWrapper& rNatNum,
                                                          sal_uInt32 CLOffset, LanguageType eLnge);

    // Return the format index of the currency format of the system locale.
    // Format is created if not already present.
    SVL_DLLPRIVATE sal_uInt32 ImpGetDefaultSystemCurrencyFormat(SvNFLanguageData& rCurrentLanguage,
                                                                const NativeNumberWrapper& rNatNum);

    SVL_DLLPRIVATE sal_uInt32 FindCachedDefaultFormat(sal_uInt32 nSearch) const;

    SVL_DLLPRIVATE static sal_Int32
    ImpGetFormatCodeIndex(const SvNFLanguageData& rCurrentLanguage,
                          css::uno::Sequence<css::i18n::NumberFormatCode>& rSeq,
                          const NfIndexTableOffset nTabOff);

    SVL_DLLPRIVATE static void ImpAdjustFormatCodeDefault(const SvNFLanguageData& rCurrentLanguage,
                                                          css::i18n::NumberFormatCode* pFormatArr,
                                                          sal_Int32 nCnt);
};

class SVL_DLLPUBLIC SvNFEngine
{
public:
    typedef std::function<sal_uInt32(SvNFLanguageData& rCurrentLanguage,
                                     const NativeNumberWrapper& rNatNum, LanguageType eLnge)>
        GetCLOffset;
    typedef std::function<void(sal_uInt32 nSearch, sal_uInt32 nFormat)> CacheFormat;
    typedef std::function<sal_uInt32(sal_uInt32 nSearch)> FindFormat;

    typedef std::function<sal_uInt32(SvNFLanguageData& rCurrentLanguage,
                                     const NativeNumberWrapper& rNatNum, sal_uInt32 CLOffset,
                                     LanguageType eLnge)>
        GetDefaultCurrency;

    struct Accessor
    {
        GetCLOffset mGetCLOffset;
        CacheFormat mCacheFormat;
        FindFormat mFindFormat;
        GetDefaultCurrency mGetDefaultCurrency;
    };

    static Accessor GetRWPolicy(SvNFFormatData& rFormatData);
    static Accessor GetROPolicy(const SvNFFormatData& rFormatData,
                                SvNFFormatData::DefaultFormatKeysMap& rFormatCache);

    static void ChangeIntl(SvNFLanguageData& rCurrentLanguage, LanguageType eLnge);
    static void ChangeNullDate(const SvNFLanguageData& rCurrentLanguage, sal_uInt16 nDay,
                               sal_uInt16 nMonth, sal_Int16 nYear);

    static sal_uInt32 GetFormatIndex(SvNFLanguageData& rCurrentLanguage, const Accessor& rFuncs,
                                     const NativeNumberWrapper& rNatNum, NfIndexTableOffset nTabOff,
                                     LanguageType eLnge);

    static sal_uInt32 GetFormatForLanguageIfBuiltIn(SvNFLanguageData& rCurrentLanguage,
                                                    const NativeNumberWrapper& rNatNum,
                                                    const Accessor& rFunc, sal_uInt32 nFormat,
                                                    LanguageType eLnge);

    static bool IsNumberFormat(SvNFLanguageData& rCurrentLanguage,
                               const SvNFFormatData& rFormatData,
                               const NativeNumberWrapper& rNatNum, const Accessor& rFunc,
                               const OUString& sString, sal_uInt32& F_Index, double& fOutNumber,
                               SvNumInputOptions eInputOptions = SvNumInputOptions::NONE);

    static sal_uInt32 GetStandardFormat(SvNFLanguageData& rCurrentLanguage,
                                        const SvNFFormatData& rFormatData,
                                        const NativeNumberWrapper& rNatNum, const Accessor& rFunc,
                                        SvNumFormatType eType, LanguageType eLnge);

    static sal_uInt32 GetStandardFormat(SvNFLanguageData& rCurrentLanguage,
                                        const SvNFFormatData& rFormatData,
                                        const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
                                        sal_uInt32 nFIndex, SvNumFormatType eType,
                                        LanguageType eLnge);

    static sal_uInt32 GetStandardFormat(SvNFLanguageData& rCurrentLanguage,
                                        const SvNFFormatData& rFormatData,
                                        const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
                                        double fNumber, sal_uInt32 nFIndex, SvNumFormatType eType,
                                        LanguageType eLnge);

    static sal_uInt32 GetStandardIndex(SvNFLanguageData& rCurrentLanguage,
                                       const SvNFFormatData& rFormatData,
                                       const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
                                       LanguageType eLnge = LANGUAGE_DONTKNOW);

    static sal_uInt32 GetTimeFormat(SvNFLanguageData& rCurrentLanguage,
                                    const SvNFFormatData& rFormatData,
                                    const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
                                    double fNumber, LanguageType eLnge, bool bForceDuration);

    /// Return the decimal separator matching the locale of the given format
    static OUString GetFormatDecimalSep(SvNFLanguageData& rCurrentLanguage,
                                        const SvNFFormatData& rFormatData, sal_uInt32 nFormat);

    /// Count of decimals
    static sal_uInt16 GetFormatPrecision(const SvNFLanguageData& rCurrentLanguage,
                                         const SvNFFormatData& rFormatData, sal_uInt32 nFormat);

    static OUString GetInputLineString(SvNFLanguageData& rCurrentLanguage,
                                       const SvNFFormatData& rFormatData,
                                       const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
                                       const double& fOutNumber, sal_uInt32 nFIndex,
                                       bool bFiltering, bool bForceSystemLocale);

    static sal_uInt32 GetEditFormat(SvNFLanguageData& rCurrentLanguage,
                                    const SvNFFormatData& rFormatData,
                                    const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
                                    double fNumber, sal_uInt32 nFIndex, SvNumFormatType eType,
                                    const SvNumberformat* pFormat, LanguageType eForLocale);

    static void GetOutputString(SvNFLanguageData& rCurrentLanguage,
                                const SvNFFormatData& rFormatData,
                                const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
                                const double& fOutNumber, sal_uInt32 nFIndex, OUString& sOutString,
                                const Color** ppColor, bool bUseStarFormat);

    static void GetOutputString(SvNFLanguageData& rCurrentLanguage,
                                const SvNFFormatData& rFormatData, const OUString& sString,
                                sal_uInt32 nFIndex, OUString& sOutString, const Color** ppColor,
                                bool bUseStarFormat);

    static bool GetPreviewString(SvNFLanguageData& rCurrentLanguage,
                                 const SvNFFormatData& rFormatData,
                                 const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
                                 const OUString& sFormatString, double fPreviewNumber,
                                 OUString& sOutString, const Color** ppColor, LanguageType eLnge,
                                 bool bUseStarFormat);

    static bool GetPreviewString(SvNFLanguageData& rCurrentLanguage,
                                 const SvNFFormatData& rFormatData,
                                 const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
                                 const OUString& sFormatString, const OUString& sPreviewString,
                                 OUString& sOutString, const Color** ppColor,
                                 LanguageType eLnge = LANGUAGE_DONTKNOW);

    static bool GetPreviewStringGuess(SvNFLanguageData& rCurrentLanguage,
                                      const SvNFFormatData& rFormatData,
                                      const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
                                      const OUString& sFormatString, double fPreviewNumber,
                                      OUString& sOutString, const Color** ppColor,
                                      LanguageType eLnge);

    static OUString GenerateFormat(SvNFLanguageData& rCurrentLanguage,
                                   const SvNFFormatData& rFormatData,
                                   const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
                                   sal_uInt32 nIndex, LanguageType eLnge, bool bThousand,
                                   bool IsRed, sal_uInt16 nPrecision, sal_uInt16 nLeadingZeros);

private:
    static sal_uInt32 ImpGetDefaultFormat(const SvNFFormatData& rFormatData, const Accessor& rFuncs,
                                          SvNumFormatType nType, sal_uInt32 CLOffset);

    static sal_uInt32 ImpGetDefaultFormat(const SvNFFormatData& rFormatData, SvNumFormatType nType,
                                          sal_uInt32 CLOffset);

    static sal_uInt32
    ImpGetStandardFormat(SvNFLanguageData& rCurrentLanguage, const SvNFFormatData& rFormatData,
                         const NativeNumberWrapper& rNatNum, const SvNFEngine::Accessor& rFuncs,
                         SvNumFormatType eType, sal_uInt32 CLOffset, LanguageType eLnge);

    static sal_uInt32 DefaultCurrencyRW(SvNFFormatData& rFormatData,
                                        SvNFLanguageData& rCurrentLanguage,
                                        const NativeNumberWrapper& rNatNum, sal_uInt32 CLOffset,
                                        LanguageType eLnge);
    static sal_uInt32 DefaultCurrencyRO(const SvNFFormatData& rFormatData, SvNFLanguageData&,
                                        const NativeNumberWrapper&, sal_uInt32 CLOffset,
                                        LanguageType eLnge);

    static sal_uInt32 GetCLOffsetRW(SvNFFormatData& rFormatData, SvNFLanguageData& rCurrentLanguage,
                                    const NativeNumberWrapper& rNatNum, LanguageType eLnge);
    static sal_uInt32 GetCLOffsetRO(const SvNFFormatData& rFormatData, SvNFLanguageData&,
                                    const NativeNumberWrapper&, LanguageType eLnge);

    static void CacheFormatRW(SvNFFormatData& rFormatData, sal_uInt32 nSearch, sal_uInt32 nFormat);
    static void CacheFormatRO(SvNFFormatData::DefaultFormatKeysMap& rMap, sal_uInt32 nSearch,
                              sal_uInt32 nFormat);

    static sal_uInt32 FindFormatRW(const SvNFFormatData& rFormatData, sal_uInt32 nSearch);
    static sal_uInt32 FindFormatRO(const SvNFFormatData& rFormatData,
                                   const SvNFFormatData::DefaultFormatKeysMap& rMap,
                                   sal_uInt32 nSearch);
};

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
