import { array_get_index_in_range } from "../collection/array/array_get_index_in_range.js";
import { assert } from "../assert.js";

const resolve_chain = [];

export class LocalizationEngine {
    /**
     *
     * @type {Object<LocaleDataset>}
     */
    #locales = {};
    /**
     *
     * @type {Object<LanguageMetadata>}
     */
    #languages = {};


    /**
     *
     * @param {string} key
     * @param {LocaleDataset} dataset
     */
    setLocaleDataset(key, dataset) {
        assert.isString(key, 'key');
        assert.defined(dataset, 'dataset');

        this.#locales[key] = dataset;
    }

    /**
     *
     * @param {LanguageMetadata} metadata
     */
    addLanguageMetadata(metadata) {
        assert.defined(metadata, 'metadata');

        this.#languages[metadata.locale] = metadata;
    }

    /**
     *
     * Resolves language and fragment key to a localized value, follows fallback links of known languages when no value found in requested language
     *
     * @param {string} language_key Locale key
     * @param {string} fragment_key Object key
     * @returns {Object|string|undefined}
     */
    resolve(language_key, fragment_key) {


        resolve_chain[0] = language_key;
        let resolve_cursor = 0;
        let resolve_chain_end = 1;

        while (resolve_cursor < resolve_chain_end) {
            const lang = resolve_chain[resolve_cursor++];

            const locale = this.#locales[lang];

            if (locale !== undefined && locale[fragment_key] !== undefined) {

                // found a hit
                return locale[fragment_key];

            } else {

                // see if there's a fallback locale
                const metadata = this.#languages[lang];

                if (metadata !== undefined) {

                    const fallbacks = metadata.fallback_languages;
                    const fallback_count = fallbacks.length;

                    for (let i = 0; i < fallback_count; i++) {

                        const fallback = fallbacks[i];

                        if (array_get_index_in_range(resolve_chain, fallback, 0, resolve_chain_end) !== -1) {
                            // circular dependency, that language was already attempted
                            continue;
                        }

                        resolve_chain[resolve_chain_end++] = fallback;

                    }

                }

            }
        }

        // chain exhausted, no result found
        return undefined;
    }

}
