import { InjectionToken, Optional, Inject, Injectable, Pipe, APP_INITIALIZER, LOCALE_ID, NgModule } from '@angular/core';
import { __awaiter, __generator, __assign, __read, __decorate, __param } from 'tslib';
import { Storage } from '@bravobit/bb-foundation/storage';
import { Languages } from '@bravobit/bb-foundation';
import { HttpClient, HttpClientModule } from '@angular/common/http';

//
// The configuration that can be passed when
// the user initializes the localize module.
//
var LocalizeConfig = /** @class */ (function () {
    function LocalizeConfig() {
        this.dictionary = [];
    }
    return LocalizeConfig;
}());

//
// This is the injection token that will be injected
// into the service so that the all localize functions
// can retrieved and used.
//
var LOCALIZE_FUNCTION = new InjectionToken('localize_function');

//
// This is the injection token that will be injected
// into the service so that the all extra dictionaries
// can retrieved and used.
//
var LOCALIZE_EXTRA_DICTIONARY = new InjectionToken('localize_extra_dictionary');

//
// This function transform the value
// to a lowercase variant of the value.
//
var LocalizeLowercaseFunction = /** @class */ (function () {
    function LocalizeLowercaseFunction() {
    }
    LocalizeLowercaseFunction.prototype.keyword = function () {
        return 'lowercase';
    };
    LocalizeLowercaseFunction.prototype.transform = function (value) {
        return (value || '').toLowerCase();
    };
    return LocalizeLowercaseFunction;
}());

//
// This function transform the value
// to a uppercase variant of the value.
//
var LocalizeUppercaseFunction = /** @class */ (function () {
    function LocalizeUppercaseFunction() {
    }
    LocalizeUppercaseFunction.prototype.keyword = function () {
        return 'uppercase';
    };
    LocalizeUppercaseFunction.prototype.transform = function (value) {
        return (value || '').toUpperCase();
    };
    return LocalizeUppercaseFunction;
}());

var ɵ0 = {
    locale: 'en-US',
    data: {
        'form-control-errors': {
            'unknown': 'The field has an unknown error.',
            'required': 'This field is required.',
            'pattern': 'Invalid format.',
            'email': 'The field does not match a valid email address.',
            'emailTaken': 'The e-mail is already taken.',
            'passwordMatch': 'The passwords don\'t match.',
            'confirm': 'The field does not match.',
            'fullNumber': 'The field should be a full number.',
            'min': 'Minimum amount should be {{min}}.',
            'max': 'Maximum amount should be {{max}}.',
            'maxFileSize': 'The file has exceed the maximum amount of {{maxSize}}.',
            'minlength': 'Expected a minimum of {{requiredLength}} characters but got {{actualLength}} characters.',
            'maxlength': 'Expected a maximum of {{requiredLength}} characters but got {{actualLength}} characters.'
        },
        'tables': {
            'search': 'Search...',
            'loading': 'Loading...',
            'empty': 'There is nothing to find here.',
            'page': 'page',
            'error_datasource': 'No data source was provided.',
            'error_unauthorized': 'You need to be authenticated to view this data.',
            'error_forbidden': 'You do not have access to view this data.',
            'error_not_found': 'The data was not found.',
            'error_server': 'A server error occurred while fetching data from the server.',
            'error_unknown': 'A unknown error occurred.'
        },
        'file-picker': {
            'choose': 'Choose file',
            'remove': 'Remove file',
            'drop': 'or drag and drop file here'
        },
        'times': {
            'tomorrow_at': 'Tomorrow at {{date}}',
            'today_at': 'Today at {{date}}',
            'yesterday_at': 'Yesterday at {{date}}'
        }
    }
};
var LOCALIZATION_EXTRA_ENGLISH = {
    provide: LOCALIZE_EXTRA_DICTIONARY,
    useValue: ɵ0,
    multi: true
};

var ɵ0$1 = {
    locale: 'nl-NL',
    data: {
        'form-control-errors': {
            'unknown': 'Het veld heeft een onbekende fout.',
            'required': 'Dit veld is verplicht.',
            'pattern': 'Ongeldig formaat.',
            'email': 'Het veld komt niet overeen met een geldig e-mailadres.',
            'emailTaken': 'Het e-mailadres is al bezet.',
            'passwordMatch': 'De wachtwoorden komen niet overeen.',
            'confirm': 'Het veld komt niet overeen.',
            'fullNumber': 'Het veld moet een volledig getal zijn.',
            'min': 'Minimum hoeveelheid moet {{min}} zijn.',
            'max': 'Maximum hoeveelheid moet {{max}} zijn.',
            'maxFileSize': 'Het bestand heeft de maximum hoeveelheid van {{maxSize}} overschreden.',
            'minlength': 'Verwachtte minimaal {{requiredLength}} tekens, maar kreeg {{actualLength}} tekens.',
            'maxlength': 'Verwachtte maximaal {{requiredLength}} tekens, maar kreeg {{actualLength}} tekens.'
        },
        'tables': {
            'search': 'Zoeken...',
            'loading': 'Aan het laden...',
            'empty': 'Er is hier niets te vinden.',
            'page': 'pagina',
            'error_datasource': 'Er is geen gegevensbron verstrekt.',
            'error_unauthorized': 'U moet ingelogd zijn om deze gegevens te bekijken.',
            'error_forbidden': 'U hebt geen toegang om deze gegevens te bekijken.',
            'error_not_found': 'De gegevens zijn niet gevonden.',
            'error_server': 'Er is een serverfout opgetreden bij het ophalen van gegevens van de server.',
            'error_unknown': 'Er is een onbekende fout opgetreden bij het ophalen van gegevens van de server.'
        },
        'file-picker': {
            'choose': 'Kies bestand',
            'remove': 'Verwijder bestand',
            'drop': 'of sleep het bestand hiernaartoe'
        },
        'times': {
            'tomorrow_at': 'Morgen om {{date}}',
            'today_at': 'Vandaag om {{date}}',
            'yesterday_at': 'Gisteren om {{date}}'
        }
    }
};
var LOCALIZATION_EXTRA_DUTCH = {
    provide: LOCALIZE_EXTRA_DICTIONARY,
    useValue: ɵ0$1,
    multi: true
};

var LocalizeMissingHandler = /** @class */ (function () {
    function LocalizeMissingHandler() {
        this.log = function (message) {
            console && console.warn && console.warn(message);
        };
    }
    LocalizeMissingHandler.prototype.handle = function (params, localize) {
        // Log to the console when the token was not found
        // if the user did not mark it as optional.
        if (!params.optional) {
            this.log("Localize: The token with name: \"" + params.token + "\" was not found.");
        }
        // Return the default token back.
        return params.token;
    };
    return LocalizeMissingHandler;
}());

var LocalizeDictionary = /** @class */ (function () {
    function LocalizeDictionary(_a, http) {
        var id = _a.id, data = _a.data, locale = _a.locale, supportedLanguages = _a.supportedLanguages;
        // Dictionary data.
        this._data = {};
        this._url = null;
        this.recursiveSearchValue = function (data, value) {
            if (typeof data === 'undefined' || typeof data === 'string') {
                return undefined;
            }
            return data[value];
        };
        this._id = id;
        this._locale = locale || 'en-US';
        this._supportedLanguages = supportedLanguages || [];
        this._http = http;
        // Set the correct variable to the url or data.
        if (typeof data === 'string') {
            this._url = data;
        }
        else {
            this._data = data;
        }
    }
    Object.defineProperty(LocalizeDictionary.prototype, "id", {
        get: function () {
            return this._id;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(LocalizeDictionary.prototype, "locale", {
        get: function () {
            return this._locale;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(LocalizeDictionary.prototype, "supportedLanguages", {
        get: function () {
            return this._supportedLanguages;
        },
        enumerable: true,
        configurable: true
    });
    LocalizeDictionary.prototype.load = function () {
        return __awaiter(this, void 0, void 0, function () {
            var result, _a;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        // If the url was not set the data was already loaded
                        // return true because it was successful.
                        if (!this._url) {
                            return [2 /*return*/, true];
                        }
                        _b.label = 1;
                    case 1:
                        _b.trys.push([1, 3, , 4]);
                        return [4 /*yield*/, this._http.get(this._url, { headers: { 'X-No-Authorization': 'yes' } })
                                .toPromise()];
                    case 2:
                        result = _b.sent();
                        // Set the data.
                        this._data = result || {};
                        this._url = null;
                        // Return true because it succeeded.
                        return [2 /*return*/, true];
                    case 3:
                        _a = _b.sent();
                        // Return false because it failed.
                        return [2 /*return*/, false];
                    case 4: return [2 /*return*/];
                }
            });
        });
    };
    LocalizeDictionary.prototype.combine = function (extraDictionary, mode) {
        if (mode === void 0) { mode = 'prepend'; }
        // Validate that the locale matches.
        if (extraDictionary.locale !== this._locale) {
            return;
        }
        // Extract the extra data.
        var extraData = extraDictionary.data || {};
        // If the selected mode is prepend
        // add the data in front.
        if (mode === 'prepend') {
            this._data = __assign(__assign({}, extraData), this._data);
            return;
        }
        // If the selected mode is append
        // add the data in the back.
        this._data = __assign(__assign({}, this._data), extraData);
    };
    LocalizeDictionary.prototype.search = function (token) {
        // Check if the token is present in the
        // data, if so return it.
        if (this._data[token]) {
            return this._data[token];
        }
        // Values might be nested in objects this means we need
        // to split them on the dot and search them recursively
        // in the data.
        var value = (token || '')
            .split('.')
            .filter(function (value) { return !!value; })
            .reduce(this.recursiveSearchValue, this._data);
        // Check if the value is a string
        // if this is true it can be returned.
        if (typeof value === 'string') {
            return value;
        }
        // This means the translation was incorrect.
        return null;
    };
    return LocalizeDictionary;
}());

var Localize = /** @class */ (function () {
    function Localize(_http, _storage, _languages, _config, _missingHandler, _functions, _extras) {
        var _this = this;
        this._http = _http;
        this._storage = _storage;
        this._languages = _languages;
        this._config = _config;
        this._missingHandler = _missingHandler;
        this._functions = _functions;
        this._extras = _extras;
        this._storageToken = 'bb-language-dictionary-id';
        this.convertToParams = function (tokenOrParams) {
            if (typeof tokenOrParams !== 'string') {
                // Destruct the params from the object.
                var token = tokenOrParams.token, optional = tokenOrParams.optional, data = tokenOrParams.data;
                // Make sure all parameters are valid
                // by type safe adding them.
                return { token: token, optional: optional || false, data: data || {} };
            }
            // If the parameter is a string fill in the blanks.
            return { token: tokenOrParams, optional: false, data: {} };
        };
        this.getMatches = function (value, substitutions) {
            // Get the matching substitutions.
            var start = substitutions.start, end = substitutions.end;
            // Compose a regex that will find the matches.
            var regex = new RegExp(start + "([^" + end + "]+)" + end, 'g');
            // Execute the regex to find matches.
            var match = regex.exec(value);
            var matches = [];
            while (match !== null) {
                // Push a new match.
                matches.push({
                    match: match[0],
                    token: match[1]
                });
                // Try to find more matches.
                match = regex.exec(value);
            }
            // Return the processed value.
            return matches.filter(function (item) { return !!item; });
        };
        this.reload = function () {
            window && window.location && window.location.reload && window.location.reload();
        };
        // Check if at least one language was passed in the config.
        if (this._config.dictionary.length < 1) {
            throw new Error('Localize: one or more languages should be supplied.');
        }
        // Gather the dictionaries.
        this._dictionaries = this._config.dictionary.map(function (item) { return new LocalizeDictionary(item, _this._http); });
        // Save the best matching current dictionary.
        this._currentDictionary = this.retrieveBestMatchingDictionary();
        // Gather all the localize functions.
        this._functionMiddleware = this._functions.reduce(function (previous, current) {
            previous[current.keyword()] = current;
            return previous;
        }, {});
    }
    Object.defineProperty(Localize.prototype, "dictionaries", {
        get: function () {
            return this._dictionaries;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Localize.prototype, "current", {
        get: function () {
            return this._currentDictionary;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Localize.prototype, "locale", {
        get: function () {
            return this._currentDictionary.locale;
        },
        enumerable: true,
        configurable: true
    });
    Localize.prototype.initialize = function () {
        var _this = this;
        return function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
            return [2 /*return*/, this.load(this._currentDictionary)];
        }); }); };
    };
    Localize.prototype.change = function (id, reload) {
        if (reload === void 0) { reload = true; }
        // Try to fetch the dictionary.
        var dictionary = this._dictionaries.find(function (item) { return item.id === id; });
        // Validate the dictionary exists.
        if (!dictionary) {
            throw new Error("Localize: Could not find dictionary with id: \"" + id + "\"");
        }
        // Save the language in the storage.
        this._storage.set(this._storageToken, dictionary.id);
        // Just quit if we don't need to reload.
        if (!reload) {
            return;
        }
        // We need to reload the application for the translations.
        return this.reload();
    };
    Localize.prototype.clear = function (reload) {
        if (reload === void 0) { reload = true; }
        // Remove the language in the local storage.
        this._storage.remove(this._storageToken);
        // Just quit if we don't need to reload.
        if (!reload) {
            return;
        }
        // We need to reload the application for the translations.
        return this.reload();
    };
    Localize.prototype.translate = function (tokenOrParams) {
        // 1. Convert to the params.
        var params = this.convertToParams(tokenOrParams);
        // 2. Try to find the translation in the dictionary.
        var searchResult = this._currentDictionary.search(params.token);
        // 3. Verify the translation was found.
        if (!searchResult) {
            return this._missingHandler.handle(params, this);
        }
        // 4. Find references in the translation that need to be translated as well.
        var processResult = this.findReferencesInTranslation(searchResult);
        // 5. Parse the interpolation data.
        return this.parseInterpolationData(processResult, params.data);
    };
    Localize.prototype.parseInterpolationData = function (value, data) {
        var _this = this;
        // Get the interpolation substitutions.
        var substitutions = this.getSubstitutions('interpolation', ['{{', '}}']);
        // Get all matches in the value.
        var matches = this.getMatches(value, substitutions);
        // Replace all matches with the parsed value.
        return matches.reduce(function (previous, current) {
            // Try to parse the token.
            var interpolatedToken = _this.interpolateFunctionData(current.token, data);
            // Replace the match with the parsed token.
            return previous.replace(current.match, interpolatedToken);
        }, "" + value);
    };
    Localize.prototype.interpolateFunctionData = function (value, interpolationData) {
        var _this = this;
        // Get the token and the methods by splitting
        // on the pipe symbol.
        var _a = __read(value.split('|').map(function (item) { return item.trim(); })), token = _a[0], methods = _a.slice(1);
        // Get the value for the token from
        // the interpolation data.
        var interpolationValue = interpolationData[token] || token;
        // Loop through all the middleware methods and try to
        // conform the string to the given parameters.
        return methods.reduce(function (string, middlewareName) {
            // Try to retrieve the middleware from the collection.
            var middleware = _this._functionMiddleware[middlewareName];
            // If the middleware is missing return an error
            // so that the user knows it was not found.
            if (!middleware) {
                return string;
            }
            // Execute the middleware function with the string.
            return middleware.transform(string);
        }, "" + interpolationValue);
    };
    Localize.prototype.findReferencesInTranslation = function (value) {
        var _this = this;
        // Get the reference substitutions.
        var substitutions = this.getSubstitutions('references', ['#(', ')']);
        // Get all matches in the value.
        var matches = this.getMatches(value, substitutions);
        // Replace all matches with the translated value.
        return matches.reduce(function (previous, current) {
            // Try to translate the token.
            var translatedToken = _this.translate(current.token);
            // Replace the match with the translated token.
            return previous.replace(current.match, translatedToken);
        }, value);
    };
    Localize.prototype.getSubstitutions = function (type, defaultValue) {
        var substitutions = (this._config
            && this._config.substitutions
            && this._config.substitutions[type]);
        // Get the substitutions.
        var value = (!substitutions || substitutions.length !== 2)
            ? defaultValue
            : substitutions;
        // Escape both substitutions so that they
        // can be used.
        var escape = /[.*+?^${}()|[\]\\]/g;
        var start = value[0].replace(escape, '\\$&');
        var end = value[1].replace(escape, '\\$&');
        // Return the substitutions.
        return { start: start, end: end };
    };
    Localize.prototype.retrieveBestMatchingDictionary = function () {
        // Get the dictionary based on storage.
        var storageDictionary = this.getDictionaryFromStorage();
        // Get the dictionary based on the supported language.
        var browserDictionary = this.getBestBrowserMatchDictionary();
        // Get the dictionary based on the first index.
        var firstDictionary = this._dictionaries[0];
        // Return the best matching dictionary.
        return storageDictionary || browserDictionary || firstDictionary;
    };
    Localize.prototype.getDictionaryFromStorage = function () {
        // Get dictionary id from the storage..
        var dictionaryId = this._storage.get(this._storageToken);
        // Return the dictionary based on the storage dictionary id.
        return this._dictionaries.find(function (item) { return item.id === dictionaryId; });
    };
    Localize.prototype.getBestBrowserMatchDictionary = function () {
        var _this = this;
        // Loop through all browser languages and return the
        // best matching dictionary.
        return this._languages.all.map(function (_a) {
            var code = _a.code, region = _a.region;
            var languageCode = [code, region].filter(function (data) { return !!data; }).join('-');
            return _this._dictionaries.find(function (item) { return item.supportedLanguages.includes(languageCode); });
        }).filter(function (item) { return !!item; }).find(function (_, index) { return index === 0; });
    };
    Localize.prototype.load = function (dictionary) {
        return __awaiter(this, void 0, void 0, function () {
            var loaded;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, dictionary.load()];
                    case 1:
                        loaded = _a.sent();
                        // Verify the data was loaded.
                        if (!loaded) {
                            throw new Error("Localize: Could not correctly fetch the data from: \"" + dictionary.id + "\"");
                        }
                        // Combine the extra dictionary data.
                        (this._extras || []).forEach(function (extraDictionary) { return dictionary.combine(extraDictionary); });
                        // Return the dictionary.
                        return [2 /*return*/, dictionary];
                }
            });
        });
    };
    Localize.ctorParameters = function () { return [
        { type: HttpClient },
        { type: Storage },
        { type: Languages },
        { type: LocalizeConfig, decorators: [{ type: Optional }] },
        { type: LocalizeMissingHandler },
        { type: Array, decorators: [{ type: Optional }, { type: Inject, args: [LOCALIZE_FUNCTION,] }] },
        { type: Array, decorators: [{ type: Optional }, { type: Inject, args: [LOCALIZE_EXTRA_DICTIONARY,] }] }
    ]; };
    Localize = __decorate([
        Injectable(),
        __param(3, Optional()),
        __param(5, Optional()), __param(5, Inject(LOCALIZE_FUNCTION)),
        __param(6, Optional()), __param(6, Inject(LOCALIZE_EXTRA_DICTIONARY))
    ], Localize);
    return Localize;
}());

var BbLocalize = /** @class */ (function () {
    function BbLocalize(_localize) {
        this._localize = _localize;
    }
    BbLocalize.prototype.transform = function (tokenOrParams, args) {
        // Validate that the localize service
        // exists in the pipe.
        if (!this._localize) {
            return tokenOrParams;
        }
        // Combine the params with the options
        // to form the localize params.
        var params = this.combineParamsWithOptions(tokenOrParams, args);
        // Try to localize the value.
        return this._localize.translate(params);
    };
    BbLocalize.prototype.combineParamsWithOptions = function (tokenOrParams, args) {
        // Gather the extras.
        var extras = args || {};
        // Loop through all the keys to find the set options.
        var options = Object.keys(extras).reduce(function (previous, current) {
            if (typeof extras[current] === 'undefined') {
                return previous;
            }
            previous[current] = extras[current];
            return previous;
        }, {});
        // Convert the string token to a params object.
        var params = typeof tokenOrParams === 'string'
            ? { token: tokenOrParams, optional: false, data: {} }
            : tokenOrParams;
        // Return the combined params and options.
        return __assign(__assign({}, params), options);
    };
    BbLocalize.ctorParameters = function () { return [
        { type: Localize, decorators: [{ type: Optional }] }
    ]; };
    BbLocalize = __decorate([
        Pipe({
            name: 'bbLocalize'
        }),
        __param(0, Optional())
    ], BbLocalize);
    return BbLocalize;
}());

var LocalizeModule = /** @class */ (function () {
    function LocalizeModule() {
    }
    LocalizeModule_1 = LocalizeModule;
    LocalizeModule.forRoot = function (config) {
        return {
            ngModule: LocalizeModule_1,
            providers: [
                // Angular providers.
                { provide: APP_INITIALIZER, deps: [Localize], useFactory: initializeLocale, multi: true },
                { provide: LOCALE_ID, deps: [Localize], useFactory: getLocale },
                // Config provider.
                { provide: LocalizeConfig, useValue: config },
                // Function providers.
                { provide: LOCALIZE_FUNCTION, useClass: LocalizeLowercaseFunction, multi: true },
                { provide: LOCALIZE_FUNCTION, useClass: LocalizeUppercaseFunction, multi: true },
                // Handler providers.
                { provide: LocalizeMissingHandler, useClass: LocalizeMissingHandler },
                // Service providers.
                Localize
            ]
        };
    };
    var LocalizeModule_1;
    LocalizeModule = LocalizeModule_1 = __decorate([
        NgModule({
            imports: [HttpClientModule],
            declarations: [BbLocalize],
            exports: [BbLocalize]
        })
    ], LocalizeModule);
    return LocalizeModule;
}());
function initializeLocale(localize) {
    return localize.initialize();
}
function getLocale(localize) {
    return localize.locale;
}

/**
 * Generated bundle index. Do not edit.
 */

export { BbLocalize, LOCALIZATION_EXTRA_DUTCH, LOCALIZATION_EXTRA_ENGLISH, LOCALIZE_EXTRA_DICTIONARY, LOCALIZE_FUNCTION, Localize, LocalizeConfig, LocalizeLowercaseFunction, LocalizeMissingHandler, LocalizeModule, LocalizeUppercaseFunction, getLocale, initializeLocale, ɵ0 };
//# sourceMappingURL=bravobit-bb-foundation-localize.js.map
