"use strict";
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
Object.defineProperty(exports, "__esModule", { value: true });
exports.ANY_LANGUAGE = void 0;
exports.i18nFromTranslations = i18nFromTranslations;
const valtio_1 = require("valtio");
const utils_1 = require("valtio/utils");
exports.ANY_LANGUAGE = 'all';
// this code is in the backend to give the chance to plugins to translate http's body
function i18nFromTranslations(translations, embedded = 'en') {
    const state = (0, valtio_1.proxy)({
        embedded,
        disabled: false,
        translations, // all dictionaries
    });
    const searchLangs = [];
    (0, utils_1.watch)(get => {
        const snap = get(state);
        searchLangs.splice(0, Infinity, exports.ANY_LANGUAGE, ...snap.disabled ? [] : Object.keys(snap.translations), snap.embedded); // replace the array completely
    });
    const warns = new Set(); // avoid duplicates
    function getLangs() {
        return Object.keys(state.translations);
    }
    // If one of the keys is an "id", that should be the first. If one of the keys should work as a fallback, that should be the last. Use 'fallback' parameter if you don't want the fallback to work as a key.
    function t(keyOrTpl, params, fallback) {
        if (!keyOrTpl)
            return '';
        // memoize?
        const keys = isTemplateStringsArray(keyOrTpl) ? [(fallback ??= keyOrTpl[0])]
            : Array.isArray(keyOrTpl) ? keyOrTpl : [keyOrTpl];
        if (typeof params === 'string' && !fallback) {
            fallback = params;
            params = null;
        }
        let found;
        let selectedLang = ''; // keep track of where we find the translation
        const { embedded } = state;
        const langs = Object.keys(state.translations);
        for (const key of keys) {
            for (const lang of searchLangs)
                if (found = state.translations[selectedLang = lang]?.translate?.[key])
                    break;
            if (found)
                break;
            if (!warns.has(key) && langs.length && langs[0] !== embedded) {
                warns.add(key);
                console.debug("i18n miss:", key);
            }
        }
        if (!found) {
            found = fallback || keys[keys.length - 1];
            selectedLang = embedded;
        }
        return Array.from(tokenizer(found)).map(([s, inside]) => {
            if (!inside)
                return s;
            const [k, cmd, rest] = s.split(',').map(x => x.trim());
            if (!params)
                throw "missing params on " + keys[0];
            const v = k && params[k];
            if (cmd === 'plural' && rest)
                return plural(v, rest);
            return v || v === 0 ? v : '';
        }).join('');
        function plural(v, rest) {
            const plural = !Intl.PluralRules ? 'other'
                : new Intl.PluralRules(selectedLang || embedded).select(Number(v));
            let other = '';
            let pickNext = false;
            let collectOther = false;
            for (const [s, inside] of tokenizer(rest)) {
                if (pickNext)
                    return pick(s);
                if (collectOther) {
                    other = s;
                    collectOther = false;
                }
                if (inside)
                    continue;
                const selectors = s.trim().split(/\s+/);
                pickNext = selectors.some(sel => sel[0] === '=' && v === Number(sel.slice(1))
                    || sel === plural);
                collectOther = !pickNext && selectors.includes('other');
            }
            return pick(other);
            function pick(s) {
                return s.replace('#', String(v));
            }
        }
    }
    function* tokenizer(s) {
        let ofs = 0;
        while (1) {
            const open = s.indexOf('{', ofs);
            if (open < 0)
                break;
            yield [s.slice(ofs, open), false];
            let stack = 1;
            ofs = open + 1;
            while (stack && ofs < s.length) {
                if (s[ofs] === '{')
                    stack++;
                else if (s[ofs] === '}')
                    stack--;
                ofs++;
            }
            if (stack)
                return console.debug('Tokenizer: unclosed'); // invalid, abort
            yield [s.slice(open + 1, ofs - 1), true];
        }
        yield [s.slice(ofs), false];
    }
    function isTemplateStringsArray(x) {
        return x?.raw && Array.isArray(x);
    }
    return {
        t,
        state,
        getLangs,
        i18nWrapperProps: () => ({ lang: getLangs()[0] || state.embedded }),
        useI18N() {
            (0, valtio_1.useSnapshot)(state);
            return { t };
        }
    };
}
