"use strict";
// Copyright IBM Corp. 2018,2020. All Rights Reserved.
// Node module: strong-globalize
// This file is licensed under the Artistic License 2.0.
// License text available at https://opensource.org/licenses/Artistic-2.0
Object.defineProperty(exports, "__esModule", { value: true });
//tslint:disable:no-any
const assert = require("assert");
const debugModule = require("debug");
const fs = require("fs");
const Globalize = require("globalize");
const yamljs_1 = require("yamljs");
const config_1 = require("./config");
const helper = require("./helper");
const helper_1 = require("./helper");
const dbg = debugModule('strong-globalize');
const osLocale = require('os-locale');
const MapCache = require('lodash/_MapCache');
const md5 = require('md5');
const memoize = require('lodash/memoize');
const pathUtil = require('path');
const util = require('util');
var helper_2 = require("./helper");
exports.setRootDir = helper_2.setRootDir;
exports.c = formatCurrency;
exports.d = formatDate;
exports.n = formatNumber;
exports.t = formatMessage;
exports.m = formatMessage;
var config_2 = require("./config");
exports.STRONGLOOP_GLB = config_2.STRONGLOOP_GLB;
/**
 * StrongLoop Defaults
 */
const SL_DEFAULT_DATETIME = { datetime: 'medium' };
const SL_DEFAULT_NUMBER = { round: 'floor' };
const SL_DEFAULT_CURRENCY = { style: 'name' };
const OS_LANG = osLanguage();
let MY_APP_LANG = process.env.STRONGLOOP_GLOBALIZE_APP_LANGUAGE;
MY_APP_LANG = helper.isSupportedLanguage(MY_APP_LANG) ? MY_APP_LANG : null;
function osLanguage() {
    const locale = osLocale.sync();
    const lang = locale.substring(0, 2);
    if (helper.isSupportedLanguage(lang))
        return lang;
    if (lang === 'zh') {
        const region = locale.substring(3);
        if (region === 'CN')
            return 'zh-Hans';
        if (region === 'TW')
            return 'zh-Hant';
        if (region === 'Hans')
            return 'zh-Hans';
        if (region === 'Hant')
            return 'zh-Hant';
    }
}
/**
 * setDefaultLanguage
 *
 * @param {string} (optional, default: `'en'`) Language ID.
 *     It tries to use OS language, then falls back to 'en'
 */
function setDefaultLanguage(lang) {
    if (lang)
        lang = helper_1.getLangAlias(lang);
    lang = helper.isSupportedLanguage(lang) ? lang : undefined;
    lang = lang || MY_APP_LANG || OS_LANG || helper.ENGLISH;
    loadGlobalize(lang);
    if (lang !== helper.ENGLISH) {
        loadGlobalize(helper.ENGLISH);
    }
    config_1.STRONGLOOP_GLB.locale(lang);
    config_1.STRONGLOOP_GLB.DEFAULT_LANG = lang;
    return lang;
}
exports.setDefaultLanguage = setDefaultLanguage;
/**
 * setAppLanguages
 *
 * @param {string} (optional, default: `[...]`) [].
 *    Sets the supported languages for the application.
 *    These should be a subset of the languages within the intl
 *    directory.
 *
 *    If no argument is passed, the function uses the contents of
 *    the intl directory to determine the application languages.
 *
 */
function setAppLanguages(langs) {
    langs = langs || readAppLanguagesSync() || [];
    config_1.STRONGLOOP_GLB.APP_LANGS = langs;
    return langs;
}
exports.setAppLanguages = setAppLanguages;
function readAppLanguagesSync() {
    try {
        const langs = fs.readdirSync(pathUtil.join(config_1.STRONGLOOP_GLB.MASTER_ROOT_DIR, 'intl'));
        return langs;
    }
    catch (ex) {
        return null;
    }
}
/**
 * Globalize.formatMessage wrapper returns a string.
 *
 * @param {string} path The message key
 * @param {object} variables List of placeholder key and content value pair.
 * @param {string} variables.<phXXX> The placeholder key.
 * @param {string} variables.<string> The content value.
 *     If the system locale is undefined, falls back to 'en'
 */
function formatMessage(path, variables, lang) {
    assert(path);
    if (!config_1.STRONGLOOP_GLB.DEFAULT_LANG)
        setDefaultLanguage();
    let message = path;
    if (helper.hashKeys(path)) {
        if (!config_1.STRONGLOOP_GLB.getHash) {
            config_1.STRONGLOOP_GLB.getHash = memoize(md5);
        }
        path = config_1.STRONGLOOP_GLB.getHash(path);
    }
    lang = lang || config_1.STRONGLOOP_GLB.DEFAULT_LANG;
    dbg('~~~ lang = %s %s %j %s', lang, path, variables, __filename);
    const trailer = helper.getTrailerAfterDot(path);
    if (trailer === 'json' || trailer === 'yml' || trailer === 'yaml') {
        const fullPath = pathUtil.join(helper.getRootDir(), path);
        return formatJson(fullPath, JSON.parse(variables), lang);
    }
    function formatMsgInline(language) {
        const g = config_1.STRONGLOOP_GLB.bundles[language];
        if (!config_1.STRONGLOOP_GLB.formatters) {
            config_1.STRONGLOOP_GLB.formatters = new MapCache();
        }
        const allFormatters = config_1.STRONGLOOP_GLB.formatters;
        let langFormatters;
        if (allFormatters.has(language)) {
            langFormatters = allFormatters.get(language);
        }
        else {
            langFormatters = new MapCache();
            allFormatters.set(language, langFormatters);
        }
        if (langFormatters.has(path))
            return langFormatters.get(path)(variables || {});
        const format = g.messageFormatter(path);
        langFormatters.set(path, format);
        return format(variables || {});
    }
    try {
        message = formatMsgInline(lang);
    }
    catch (e) {
        if (lang === helper.ENGLISH) {
            message = sanitizeMsg(message, variables);
            dbg('*** %s not found for %s. Fall back to: "%s" ***  %s', path, lang, message, e);
        }
        else {
            dbg('*** %s for %s not localized. Fall back to English. ***  %s', path, lang, e);
            try {
                message = formatMsgInline(helper.ENGLISH);
            }
            catch (e) {
                message = sanitizeMsg(message, variables);
                dbg('*** %s not found for %s. Fall back to: "%s" ***  %s', path, lang, message, e);
            }
        }
    }
    if (config_1.STRONGLOOP_GLB.PSEUDO_LOC_PREAMBLE) {
        message = config_1.STRONGLOOP_GLB.PSEUDO_LOC_PREAMBLE + message;
    }
    return message;
}
exports.formatMessage = formatMessage;
function formatJson(fullPath, variables, lang) {
    assert(fullPath === pathUtil.resolve(helper.getRootDir(), fullPath), '*** full path is required to format json/yaml file: ' + fullPath);
    const fileType = helper.getTrailerAfterDot(fullPath);
    let jsonData = null;
    try {
        const contentStr = fs.readFileSync(fullPath, 'utf8');
        if (fileType === 'json')
            jsonData = JSON.parse(contentStr);
        if (fileType === 'yml' || fileType === 'yaml')
            jsonData = yamljs_1.parse(contentStr);
    }
    catch (_e) {
        return '*** read failure: ' + fullPath;
    }
    const msgs = helper.scanJson(variables, jsonData);
    const transMsgs = [];
    msgs.forEach(function (msg) {
        const transMsg = formatMessage(msg, undefined, lang);
        transMsgs.push(transMsg);
    });
    helper.replaceJson(variables, jsonData, transMsgs);
    return jsonData;
}
exports.formatJson = formatJson;
function sanitizeMsg(message, variables) {
    message = message.replace(/}}/g, '').replace(/{{/g, '');
    if (typeof variables === 'string' ||
        (Array.isArray(variables) && variables.length > 0)) {
        const sanitizedMsg = message.replace(/%[sdj]/g, '%s');
        message = util.format.apply(util, [sanitizedMsg].concat(variables));
    }
    return message;
}
function packMessage(args, fn, withOriginalMsg, lang) {
    const path = args[0];
    const percentInKey = helper.percent(path);
    const txtWithTwoOrMoreArgs = helper.getTrailerAfterDot(path) === 'txt' && args.length > 2;
    // If it comes from *.txt, there are no percent in the path,
    // but there can be one or more %s in the message value.
    const variables = percentInKey
        ? helper.mapArgs(path, args)
        : txtWithTwoOrMoreArgs
            ? helper.repackArgs(args, 1)
            : args[1];
    let message = formatMessage(path, variables, lang);
    if (withOriginalMsg)
        message = {
            language: lang,
            message: message,
            orig: path,
            vars: variables,
        };
    if (fn)
        return fn(message);
    return message;
}
exports.packMessage = packMessage;
/* RFC 5424 Syslog Message Severities
 * 0 Emergency: system is unusable
 * 1 Alert: action must be taken immediately
 * 2 Critical: critical conditions
 * 3 Error: error conditions
 * 4 Warning: warning conditions
 * 5 Notice: normal but significant condition
 * 6 Informational: informational messages
 * 7 Debug: debug-level messages
 */
function rfc5424(level, args, print, lang) {
    return packMessage(args, (msg) => {
        logPersistent(level, msg);
        if (consoleEnabled())
            print(msg.message);
        return msg;
    }, true, lang);
}
exports.rfc5424 = rfc5424;
function myError(...args) {
    const msg = packMessage(args, null, true);
    logPersistent('error', msg);
    return new Error(msg.message);
}
module.exports.Error = myError;
// RFC 5424 Syslog Message Severities
function emergency(...args) {
    return rfc5424('emergency', args, console.error);
}
exports.emergency = emergency;
function alert(...args) {
    return rfc5424('alert', args, console.error);
}
exports.alert = alert;
function critical(...args) {
    return rfc5424('critical', args, console.error);
}
exports.critical = critical;
function error(...args) {
    return rfc5424('error', args, console.error);
}
exports.error = error;
function warning(...args) {
    return rfc5424('warning', args, console.error);
}
exports.warning = warning;
function notice(...args) {
    return rfc5424('notice', args, console.log);
}
exports.notice = notice;
function informational(...args) {
    return rfc5424('informational', args, console.log);
}
exports.informational = informational;
function debug(...args) {
    return rfc5424('debug', args, console.log);
}
exports.debug = debug;
function warn(...args) {
    return rfc5424('warn', args, console.error);
}
exports.warn = warn;
function info(...args) {
    return rfc5424('info', args, console.log);
}
exports.info = info;
function log(...args) {
    return rfc5424('log', args, console.log);
}
exports.log = log;
function help(...args) {
    return rfc5424('help', args, console.log);
}
exports.help = help;
function data(...args) {
    return rfc5424('data', args, console.log);
}
exports.data = data;
function prompt(...args) {
    return rfc5424('prompt', args, console.log);
}
exports.prompt = prompt;
function verbose(...args) {
    return rfc5424('verbose', args, console.log);
}
exports.verbose = verbose;
function input(...args) {
    return rfc5424('input', args, console.log);
}
exports.input = input;
function silly(...args) {
    return rfc5424('silly', args, console.log);
}
exports.silly = silly;
/**
 * Globalize.formatNumber wrapper returns a string.
 *
 * @param {value} integer or float
 * @param {object} The options (optional); if null, use the StrongLoop default.
 *     Strongly recommended to set NO options and let strong-globalize use
 *     the StrongLoop default for consistency across StrongLoop products.
 *     See https://www.npmjs.com/package/globalize#number-module
 */
function formatNumber(value, options, lang) {
    assert(value);
    if (!config_1.STRONGLOOP_GLB.DEFAULT_LANG)
        setDefaultLanguage(lang);
    lang = (lang || config_1.STRONGLOOP_GLB.DEFAULT_LANG);
    options = options || SL_DEFAULT_NUMBER;
    const G = config_1.STRONGLOOP_GLB.bundles[lang];
    let msg = null;
    try {
        msg = G.formatNumber(value, options);
    }
    catch (e) {
        msg = value.toString();
        dbg('*** formatNumber error: value:%s', msg);
    }
    return msg;
}
exports.formatNumber = formatNumber;
/**
 * Globalize.formatDate wrapper returns a string.
 *
 * @param {Date object} such as new Date()
 * @param {object} The options (optional); if null, use the StrongLoop default.
 *     Strongly recommended to set NO options and let strong-globalize use
 *     the StrongLoop default for consistency across StrongLoop products.
 *     See https://www.npmjs.com/package/globalize#date-module
 */
function formatDate(value, options, lang) {
    assert(value);
    if (!config_1.STRONGLOOP_GLB.DEFAULT_LANG)
        setDefaultLanguage(lang);
    lang = (lang || config_1.STRONGLOOP_GLB.DEFAULT_LANG);
    options = options || SL_DEFAULT_DATETIME;
    const G = config_1.STRONGLOOP_GLB.bundles[lang];
    let msg = null;
    try {
        msg = G.formatDate(value, options);
    }
    catch (e) {
        msg = value.toString();
        dbg('*** formatDate error: value:%s', msg);
    }
    return msg;
}
exports.formatDate = formatDate;
/**
 * Globalize.formatCurrency wrapper returns a string.
 *
 * @param {value} integer or float
 * @param {string} three-letter currency symbol, ISO 4217 Currency Code
 * @param {object} The options (optional); if null, use the StrongLoop default.
 *     Strongly recommended to set NO options and let strong-globalize use
 *     the StrongLoop default for consistency across StrongLoop products.
 *     See https://www.npmjs.com/package/globalize#curency-module
 */
function formatCurrency(value, currencySymbol, options, lang) {
    assert(value && currencySymbol);
    if (!config_1.STRONGLOOP_GLB.DEFAULT_LANG)
        setDefaultLanguage(lang);
    lang = lang || config_1.STRONGLOOP_GLB.DEFAULT_LANG;
    options = options || SL_DEFAULT_CURRENCY;
    const G = config_1.STRONGLOOP_GLB.bundles[lang];
    let msg = null;
    try {
        msg = G.formatCurrency(value, currencySymbol, options);
    }
    catch (e) {
        msg = currencySymbol.toString() + value.toString();
        dbg('*** formatCurrency error: value:%s, currencySymbol:%s', value, currencySymbol);
    }
    return msg;
}
exports.formatCurrency = formatCurrency;
function loadGlobalize(lang, enumerateNodeModules) {
    assert(helper.isSupportedLanguage(lang), 'Not supported: ' + lang);
    if (!config_1.STRONGLOOP_GLB.versionSG) {
        const versionSG = helper.getPackageVersion(pathUtil.join(__dirname, '..'));
        const versionG = helper.getPackageVersion(pathUtil.join(__dirname, '..', 'node_modules', 'globalize'));
        Object.assign(config_1.STRONGLOOP_GLB, {
            versionSG: versionSG,
            versionG: versionG,
            bundles: {},
            formatters: new MapCache(),
            getHash: memoize(md5),
            load: Globalize.load,
            locale: Globalize.locale,
            loadMessages: Globalize.loadMessages,
            DEFAULT_LANG: helper.ENGLISH,
            APP_LANGS: readAppLanguagesSync(),
            LOG_FN: null,
            DISABLE_CONSOLE: false,
            MASTER_ROOT_DIR: helper.getRootDir(),
            MSG_RES_LOADED: [],
            AUTO_MSG_LOADING: helper.AML_DEFAULT,
            PSEUDO_LOC_PREAMBLE: process.env.STRONG_GLOBALIZE_PSEUDO_LOC_PREAMBLE || '',
        });
        loadCldr(helper.ENGLISH);
        config_1.STRONGLOOP_GLB.bundles[helper.ENGLISH] = new Globalize(helper.ENGLISH);
        config_1.STRONGLOOP_GLB.locale(helper.ENGLISH);
        helper.loadMsgFromFile(helper.ENGLISH, helper.getRootDir(), enumerateNodeModules);
    }
    if (!(lang in config_1.STRONGLOOP_GLB.bundles)) {
        loadCldr(lang);
        config_1.STRONGLOOP_GLB.bundles[lang] = new Globalize(lang);
        config_1.STRONGLOOP_GLB.locale(lang);
        helper.loadMsgFromFile(lang, helper.getRootDir(), enumerateNodeModules);
    }
    return config_1.STRONGLOOP_GLB.bundles[lang];
}
exports.loadGlobalize = loadGlobalize;
function loadCldr(lang) {
    assert(config_1.STRONGLOOP_GLB &&
        (!config_1.STRONGLOOP_GLB.bundles || !config_1.STRONGLOOP_GLB.bundles[lang]), 'CLDR already loaded for ' + lang);
    const cldrDir = pathUtil.join(__dirname, '..', 'cldr');
    helper.enumerateFilesSync(cldrDir, null, ['json'], false, false, function (content, filePath) {
        let cldr = null;
        try {
            cldr = JSON.parse(content);
        }
        catch (e) {
            throw new Error('*** CLDR read error on ' + process.platform);
        }
        const cldrMain = { main: {} };
        cldrMain.main[lang] = cldr.main[lang];
        config_1.STRONGLOOP_GLB.load(cldrMain);
        if (lang === helper.ENGLISH) {
            const cldrSupplemental = { supplemental: cldr.supplemental };
            config_1.STRONGLOOP_GLB.load(cldrSupplemental);
        }
    });
}
/**
 *
 * Persistent logging
 *
 */
// const syslogLevels = { // RFC5424
//   emerg: 0,
//   alert: 1,
//   crit: 2,
//   error: 3,
//   warning: 4,
//   notice: 5,
//   info: 6,
//   debug: 7,
// };
// const npmLevels = {
//   error: 0,
//   warn: 1,
//   info: 2,
//   verbose: 3,
//   debug: 4,
//   silly: 5,
// };
function consoleEnabled() {
    if (!config_1.STRONGLOOP_GLB.DEFAULT_LANG)
        setDefaultLanguage();
    return !config_1.STRONGLOOP_GLB.DISABLE_CONSOLE;
}
exports.consoleEnabled = consoleEnabled;
/**
 *
 */
function setPersistentLogging(logFn, disableConsole) {
    assert(logFn);
    assert(typeof logFn === 'function');
    if (!config_1.STRONGLOOP_GLB.DEFAULT_LANG)
        setDefaultLanguage();
    config_1.STRONGLOOP_GLB.DISABLE_CONSOLE = !!disableConsole;
    try {
        const message = 'StrongGlobalize persistent logging started at ' + new Date();
        logFn('info', {
            language: helper.ENGLISH,
            message: message,
            orig: message,
            vars: [],
        });
        config_1.STRONGLOOP_GLB.LOG_FN = logFn;
    }
    catch (e) {
        config_1.STRONGLOOP_GLB.LOG_FN = undefined;
    }
}
exports.setPersistentLogging = setPersistentLogging;
function logPersistent(level = 'info', message) {
    if (!config_1.STRONGLOOP_GLB.DEFAULT_LANG)
        setDefaultLanguage();
    if (!config_1.STRONGLOOP_GLB.LOG_FN)
        return;
    level = level || 'info';
    if (typeof config_1.STRONGLOOP_GLB.LOG_FN === 'function') {
        config_1.STRONGLOOP_GLB.LOG_FN(level, message);
    }
}
exports.logPersistent = logPersistent;
//# sourceMappingURL=globalize.js.map