import { __awaiter, __generator, __read, __values } from "tslib";
import { Arr, Limiter, Obj, Http } from "@intelligentgraphics/ig.utilities";
import { IGXR } from "@intelligentgraphics/igd.core";
import Logger from "../Logger";
import { addIGXR, parseIGXR } from "./Util";
/** Loader for IGXR data. */
var IGXRLoader = /** @class */ (function () {
    /** Constructor. */
    function IGXRLoader(igxrData) {
        var e_1, _a;
        this.intializePromises = {};
        if (!igxrData || !igxrData.length) {
            throw new Error("Expected an array of at least one igxr object");
        }
        parseIGXR(igxrData[0]);
        this._igxr = igxrData[0];
        try {
            for (var _b = __values(igxrData.slice(1)), _c = _b.next(); !_c.done; _c = _b.next()) {
                var raw = _c.value;
                try {
                    addIGXR(this._igxr, raw);
                }
                catch (err) {
                    console.error("Encountered errors when parsing igxr. Skipping", raw);
                }
            }
        }
        catch (e_1_1) { e_1 = { error: e_1_1 }; }
        finally {
            try {
                if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
            }
            finally { if (e_1) throw e_1.error; }
        }
        if (!this._igxr) {
            var tLogMsg = "no IGXR data given";
            Logger.fatal("IGXRLoader:", tLogMsg);
            throw new Error(tLogMsg);
        }
        this.registerCatalogs = this.registerCatalogs.bind(this);
    }
    IGXRLoader.prototype.addIGXR = function (igxrData) {
        addIGXR(this._igxr, igxrData);
    };
    IGXRLoader.prototype.loadCatalog = function (sourceId, registry, connectionManager, localizationManager) {
        return this.registerCatalogs([sourceId], registry, connectionManager, localizationManager).then(function (registeredIds) { return registeredIds[0]; });
    };
    IGXRLoader.prototype.loadCatalogs = function (sourceIds, registry, connectionManager, localizationManager) {
        return this.registerCatalogs(sourceIds, registry, connectionManager, localizationManager);
    };
    IGXRLoader.prototype.addAllCatalogsToSession = function (session) {
        var catalogs = this.getCatalogList();
        return this.registerCatalogs(catalogs.map(function (cat) { return cat.ManufacturerId + "." + cat.CatalogId; }), session, session, session);
    };
    /**
     * Registers services for the specified catalogs inside the session
     *
     * @param {Session} session
     * @param {CatalogIdentifier[]} catalogs
     * @returns
     */
    IGXRLoader.prototype.addCatalogsToSession = function (catalogs, session) {
        return this.registerCatalogs(catalogs, session, session, session);
    };
    IGXRLoader.prototype.addCatalogToSession = function (catalog, session) {
        return this.addCatalogsToSession([catalog], session).then(function (_a) {
            var _b = __read(_a, 1), id = _b[0];
            return id;
        });
    };
    /**
     * Register selected catalogs in the Dispatcher.
     * @param catalogs A list of catalogs Ids.
     */
    IGXRLoader.prototype.registerCatalogs = function (catalogs, registry, connectionManager, localizationManager) {
        return __awaiter(this, void 0, void 0, function () {
            var ids, uniqueIds, idMap, queue;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        ids = catalogs.map(function (cat) {
                            var _a = _this.extractCatalogAndManufacturerId(cat), manufacturerId = _a.manufacturerId, catalogId = _a.catalogId;
                            return manufacturerId + "." + catalogId;
                        });
                        uniqueIds = Arr.unique(ids);
                        idMap = {};
                        queue = new Limiter(5).queue;
                        return [4 /*yield*/, Promise.all(uniqueIds.map(function (id) { return __awaiter(_this, void 0, void 0, function () {
                                var _a, man, cat, dispatcherId;
                                var _this = this;
                                return __generator(this, function (_b) {
                                    switch (_b.label) {
                                        case 0:
                                            _a = __read(id.split("."), 2), man = _a[0], cat = _a[1];
                                            return [4 /*yield*/, queue(function () {
                                                    return _this.registerCatalog(man, cat, registry, connectionManager, localizationManager).catch(function (err) {
                                                        Logger.error("Could not register catalog " + man + "." + cat + ".", err);
                                                        return undefined;
                                                    });
                                                })];
                                        case 1:
                                            dispatcherId = _b.sent();
                                            if (dispatcherId !== undefined) {
                                                idMap[id] = dispatcherId;
                                            }
                                            return [2 /*return*/];
                                    }
                                });
                            }); }))];
                    case 1:
                        _a.sent();
                        return [2 /*return*/, ids.map(function (id) { return idMap[id]; })];
                }
            });
        });
    };
    IGXRLoader.prototype.registerCatalog = function (manufacturerId, catalogId, registry, connectionManager, localizationManager) {
        var _this = this;
        var sourceId = manufacturerId + "." + catalogId;
        if (registry.hasSource(sourceId)) {
            return Promise.resolve(sourceId);
        }
        // if the same catalog is already being instantiated, skip this funtion and return the other promise.
        if (this.intializePromises[sourceId]) {
            return this.intializePromises[sourceId];
        }
        // use async iife to create a new promise for this catalog and add it to the list of currently loading catalogs
        return (this.intializePromises[sourceId] = (function () { return __awaiter(_this, void 0, void 0, function () {
            var catalog, serverType, backend, _a, catalogManager, productDataManager, multiArticles, configurationService;
            var _this = this;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        catalog = this.findCatalog(manufacturerId, catalogId).catalog;
                        serverType = this._igxr.Servers[catalog.Server].Type;
                        return [4 /*yield*/, registry.loadBackend(serverType)];
                    case 1:
                        backend = _b.sent();
                        if (!backend) {
                            throw new Error("No backend has been registered for type: " + serverType);
                        }
                        return [4 /*yield*/, backend.initializeManagers({
                                catalog: catalog,
                                fetchData: function (url) {
                                    return _this.loadExternalData(sourceId, url);
                                },
                                dispatcherId: sourceId,
                            }, connectionManager, localizationManager)];
                    case 2:
                        _a = _b.sent(), catalogManager = _a.catalogManager, productDataManager = _a.productManager, multiArticles = _a.multiArticles, configurationService = _a.configurationService;
                        registry.registerSource({
                            sourceId: sourceId,
                            serverType: serverType,
                            catalogManager: catalogManager,
                            configurationService: configurationService,
                            productDataManager: productDataManager,
                            multiArticles: multiArticles,
                        });
                        delete this.intializePromises[sourceId];
                        return [2 /*return*/, sourceId];
                }
            });
        }); })());
    };
    Object.defineProperty(IGXRLoader.prototype, "IGXR", {
        /**
         * returns the merged IGXR data
         */
        get: function () {
            return this._igxr;
        },
        enumerable: true,
        configurable: true
    });
    IGXRLoader.prototype.getServerType = function (serverId) {
        var server = this._igxr.Servers[serverId];
        if (server) {
            return server.Type;
        }
        Logger.error("IGXRLoader.getServerType(): unknown server", serverId);
        return undefined;
    };
    IGXRLoader.prototype.getCatalog = function (identifier) {
        var _a = this.extractCatalogAndManufacturerId(identifier), catalogId = _a.catalogId, manufacturerId = _a.manufacturerId;
        var manufacturer = this._igxr.Manufacturers[manufacturerId];
        if (manufacturer) {
            var catalog = manufacturer.Catalogs[catalogId];
            if (catalog) {
                return catalog;
            }
        }
        Logger.error("IGXRLoader.getCatalog(): unknown catalog", manufacturerId, catalogId);
        return undefined;
    };
    IGXRLoader.prototype.findOne = function (haystack, arr) {
        return arr.some(function (v) {
            return haystack.indexOf(v) >= 0;
        });
    };
    IGXRLoader.prototype.getAllManufacturers = function (language, filterTags) {
        var _this = this;
        if (language === void 0) { language = "any"; }
        var arr = [];
        Obj.iter(this._igxr.Manufacturers, function (manufacturerName, manufacturer) {
            var manuKeys = manufacturer.Keywords
                ? manufacturer.Keywords[language] || manufacturer.Keywords.any
                : undefined;
            if (!filterTags ||
                (manuKeys && _this.findOne(manuKeys, filterTags))) {
                arr.push(manufacturer.Name[language] || manufacturer.Name.any);
            }
        });
        return arr;
    };
    IGXRLoader.prototype.getAllCatalogTags = function (language) {
        if (language === void 0) { language = "any"; }
        var arr = [];
        Obj.iter(this._igxr.Manufacturers, function (index, manufacturer) {
            Obj.iter(manufacturer.Catalogs, function (indexCat, catalog) {
                if (catalog.Keywords) {
                    arr = Arr.unique(arr.concat(catalog.Keywords[language] || catalog.Keywords.any));
                }
            });
        });
        return arr;
    };
    /**
     * Returns the entries of the IGXR as a straightened list, optionally filtered.
     * @param language Language to use
     * @param filterManufacturers manufacturer names (in /language/)
     * @param filterTags category names (in /language/)
     */
    IGXRLoader.prototype.getCatalogList = function (language, filterManufacturers, filterTags, sorted) {
        if (language === void 0) { language = "any"; }
        if (filterManufacturers === void 0) { filterManufacturers = []; }
        if (filterTags === void 0) { filterTags = []; }
        if (sorted === void 0) { sorted = false; }
        var results = [];
        for (var manufacturerId in this._igxr.Manufacturers) {
            var manufacturer = this._igxr.Manufacturers[manufacturerId];
            var manufacturerName = IGXR.getLocalizedText(manufacturer.Name, language);
            if (filterManufacturers.length > 0 &&
                filterManufacturers.indexOf(manufacturerName) === -1) {
                continue;
            }
            for (var catalogId in manufacturer.Catalogs) {
                var catalog = manufacturer.Catalogs[catalogId];
                var keywords = void 0;
                if (catalog.Keywords) {
                    keywords =
                        IGXR.getLocalizedText(catalog.Keywords, language) || [];
                }
                else {
                    keywords = [];
                }
                if (filterTags.length > 0 &&
                    !this.findOne(keywords, filterTags)) {
                    continue;
                }
                var catalogEntry = {
                    CatalogId: catalogId,
                    ManufacturerId: manufacturerId,
                    CatalogName: catalog.Name[language] || catalog.Name["any"],
                    ManufacturerName: manufacturerName,
                    Keywords: keywords,
                    Image: catalog.Image,
                    SourceId: manufacturerId + "." + catalogId,
                    Hidden: catalog.Hidden === true || manufacturer.Hidden === true,
                };
                results.push(catalogEntry);
            }
        }
        if (sorted) {
            results.sort(function (a, b) {
                return (a.ManufacturerName +
                    "." +
                    a.CatalogName).toUpperCase() <
                    (b.ManufacturerName + "." + b.CatalogName).toUpperCase()
                    ? -1
                    : 1;
            });
        }
        return results;
    };
    /**
     * Retrieves informations for the specified catalog identifier
     *
     * @param {CatalogIdentifier} catalogIdentifier
     * @param {string} [language="any"]
     * @returns {IGXRCatalogListEntry}
     */
    IGXRLoader.prototype.getCatalogInfo = function (catalogIdentifier, language) {
        if (language === void 0) { language = "any"; }
        var _a = this.extractCatalogAndManufacturerId(catalogIdentifier), catalogId = _a.catalogId, manufacturerId = _a.manufacturerId;
        var _b = this.findCatalog(manufacturerId, catalogId), manufacturer = _b.manufacturer, catalog = _b.catalog;
        var keywords;
        if (catalog.Keywords !== undefined) {
            keywords = IGXR.getLocalizedText(catalog.Keywords, language) || [];
        }
        else {
            keywords = [];
        }
        return {
            CatalogId: catalogId,
            CatalogName: IGXR.getLocalizedText(catalog.Name, language),
            ManufacturerId: manufacturerId,
            ManufacturerName: IGXR.getLocalizedText(manufacturer.Name, language),
            SourceId: manufacturerId + "." + catalogId,
            Keywords: keywords,
            Image: catalog.Image,
            Hidden: catalog.Hidden === true || manufacturer.Hidden === true,
        };
    };
    IGXRLoader.prototype.getWizard = function (catalogIdentifier, language) {
        if (language === void 0) { language = "any"; }
        var _a = this.extractCatalogAndManufacturerId(catalogIdentifier), manufacturerId = _a.manufacturerId, catalogId = _a.catalogId;
        var catalog = this.findCatalog(manufacturerId, catalogId).catalog;
        if (catalog.Settings === undefined ||
            catalog.Settings.Wizard === undefined) {
            return undefined;
        }
        return {
            Name: catalog.Settings.Wizard.Name === undefined
                ? undefined
                : IGXR.getLocalizedText(catalog.Settings.Wizard.Name, language),
            Image: catalog.Settings.Wizard.Image,
            LoadPath: catalog.Settings.Wizard.LoadPath,
            Options: catalog.Settings.Wizard.Options,
        };
    };
    IGXRLoader.prototype.findCatalog = function (manufacturerId, catalogId) {
        var manufacturer = this._igxr.Manufacturers[manufacturerId];
        if (manufacturer === undefined) {
            throw new Error("Could not find a manufacturer for id \"" + manufacturerId + "\"");
        }
        var catalog = manufacturer.Catalogs[catalogId];
        if (catalog === undefined) {
            throw new Error("Could not find a catalog \"" + catalogId + "\" inside manufacturer \"" + manufacturerId + "\"");
        }
        return {
            manufacturer: manufacturer,
            catalog: catalog,
        };
    };
    IGXRLoader.prototype.loadExternalData = function (source, url) {
        return Http.getJSON(url);
    };
    IGXRLoader.prototype.extractCatalogAndManufacturerId = function (catalogIdentifier) {
        if (typeof catalogIdentifier === "object") {
            if ("CatalogId" in catalogIdentifier) {
                return {
                    catalogId: catalogIdentifier.CatalogId,
                    manufacturerId: catalogIdentifier.ManufacturerId,
                };
            }
            return catalogIdentifier;
        }
        var _a = __read(catalogIdentifier.split("."), 2), manufacturerId = _a[0], catalogId = _a[1];
        if (catalogId === undefined) {
            throw new Error("Could not parse catalog id and manufacturer id from registry id");
        }
        return {
            manufacturerId: manufacturerId,
            catalogId: catalogId,
        };
    };
    return IGXRLoader;
}());
export { IGXRLoader };
