"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var IGXB_1 = require("./IGXB");
var Logger_1 = require("./Logger");
var Utilities_1 = require("./Utilities");
var buildObjectTree = function (items) {
    var e_1, _a, e_2, _b;
    var articlesByComId = new Map();
    var roots = [];
    try {
        for (var items_1 = tslib_1.__values(items), items_1_1 = items_1.next(); !items_1_1.done; items_1_1 = items_1.next()) {
            var item = items_1_1.value;
            var result = tslib_1.__assign(tslib_1.__assign({}, item), { children: [], originalItem: item });
            articlesByComId.set(item.comId, result);
            if (item.parent === undefined) {
                roots.push(result);
            }
        }
    }
    catch (e_1_1) { e_1 = { error: e_1_1 }; }
    finally {
        try {
            if (items_1_1 && !items_1_1.done && (_a = items_1.return)) _a.call(items_1);
        }
        finally { if (e_1) throw e_1.error; }
    }
    try {
        for (var items_2 = tslib_1.__values(items), items_2_1 = items_2.next(); !items_2_1.done; items_2_1 = items_2.next()) {
            var article = items_2_1.value;
            if (article.parent === undefined) {
                continue;
            }
            var parent_1 = articlesByComId.get(article.parent);
            parent_1.children.push(articlesByComId.get(article.comId));
        }
    }
    catch (e_2_1) { e_2 = { error: e_2_1 }; }
    finally {
        try {
            if (items_2_1 && !items_2_1.done && (_b = items_2.return)) _b.call(items_2);
        }
        finally { if (e_2) throw e_2.error; }
    }
    return roots;
};
var restoreFields = ["placement", "data"];
var applyRestoreFields = function (restoreData, createdData) {
    var e_3, _a;
    try {
        for (var restoreFields_1 = tslib_1.__values(restoreFields), restoreFields_1_1 = restoreFields_1.next(); !restoreFields_1_1.done; restoreFields_1_1 = restoreFields_1.next()) {
            var key = restoreFields_1_1.value;
            if (restoreData[key] !== undefined) {
                createdData.originalItem[key] = restoreData[key];
            }
        }
    }
    catch (e_3_1) { e_3 = { error: e_3_1 }; }
    finally {
        try {
            if (restoreFields_1_1 && !restoreFields_1_1.done && (_a = restoreFields_1.return)) _a.call(restoreFields_1);
        }
        finally { if (e_3) throw e_3.error; }
    }
    // TODO: should match articles:
    // 1. by com id
    // 2. by article id
    // 3. by remaining index
    for (var index = 0; index < restoreData.children.length; index++) {
        applyRestoreFields(restoreData.children[index], createdData.children[index]);
    }
};
exports.createProjectManager = function (serviceManager, articleQuery, articleManager, operationResultHandler, hooks, registry, connectionManager, localizationManager) {
    var restoreArticles = function (articles) { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
        var results, processArticle, articleTree;
        return tslib_1.__generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    results = [];
                    processArticle = function (article) { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
                        var operator, operationResults, _a, mainResult;
                        return tslib_1.__generator(this, function (_b) {
                            switch (_b.label) {
                                case 0:
                                    operator = serviceManager.getOperatorForSourceId(article.source);
                                    return [4 /*yield*/, operator.restoreArticle(article)];
                                case 1:
                                    operationResults = (_b.sent());
                                    _a = tslib_1.__read(buildObjectTree(operationResults), 1), mainResult = _a[0];
                                    applyRestoreFields(article, mainResult);
                                    results.push.apply(results, tslib_1.__spread(operationResults));
                                    return [2 /*return*/];
                            }
                        });
                    }); };
                    articleTree = buildObjectTree(articles);
                    return [4 /*yield*/, Promise.all(articleTree.map(processArticle))];
                case 1:
                    _a.sent();
                    return [2 /*return*/, results];
            }
        });
    }); };
    var restoreFromArticleList = function (articles, catalogRegistrator) { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
        var results;
        return tslib_1.__generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    if (!(catalogRegistrator !== undefined)) return [3 /*break*/, 2];
                    return [4 /*yield*/, catalogRegistrator.loadCatalogs(Array.from(new Set(articles.map(function (article) { return article.source; }))), registry, connectionManager, localizationManager)];
                case 1:
                    _a.sent();
                    _a.label = 2;
                case 2: return [4 /*yield*/, restoreArticles(articles)];
                case 3:
                    results = _a.sent();
                    return [2 /*return*/, operationResultHandler.processOperationResults(results)];
            }
        });
    }); };
    var saveAsArticleList = function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
        return tslib_1.__generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, hooks.beforeSave.emit()];
                case 1:
                    _a.sent();
                    return [2 /*return*/, articleQuery.getAllArticles()];
            }
        });
    }); };
    var restoreFromIGXB = function (data, _a) {
        var _b = _a === void 0 ? {} : _a, catalogRegistrator = _b.catalogRegistrator, emptySession = _b.emptySession;
        return tslib_1.__awaiter(void 0, void 0, void 0, function () {
            var articles, serviceRestoreResults, _loop_1, serviceRestoreResults_1, serviceRestoreResults_1_1, result, allRestoreResult, _c;
            var e_4, _d;
            return tslib_1.__generator(this, function (_e) {
                switch (_e.label) {
                    case 0:
                        articles = IGXB_1.readIGXB(data);
                        if (!(catalogRegistrator !== undefined)) return [3 /*break*/, 2];
                        return [4 /*yield*/, catalogRegistrator.loadCatalogs(Array.from(new Set(articles.map(function (article) { return article.source; }))), registry, connectionManager, localizationManager)];
                    case 1:
                        _e.sent();
                        _e.label = 2;
                    case 2:
                        if (!emptySession) return [3 /*break*/, 4];
                        return [4 /*yield*/, articleManager.deleteArticles(Utilities_1.ArticleList.convertToComIds(articleQuery.getFilteredArticles(function (article) { return article.parent === undefined; })))];
                    case 3:
                        _e.sent();
                        _e.label = 4;
                    case 4:
                        if (!(data.SessionInfo !== undefined)) return [3 /*break*/, 6];
                        return [4 /*yield*/, restoreServices(data.SessionInfo, function (sourceId, error) {
                                return Logger_1.default.warn("Could not restore session for source \"" + sourceId + "\": " + error);
                            }, catalogRegistrator)];
                    case 5:
                        serviceRestoreResults = (_e.sent());
                        _loop_1 = function (result) {
                            var e_5, _a;
                            var articleDataIndex = articles.findIndex(function (article) { return article.comId === result.comId; });
                            if (articleDataIndex === -1) {
                                return "continue";
                            }
                            var article = articles[articleDataIndex];
                            try {
                                // copy all instance relevant properties from the igxb article data (placement and data)
                                for (var restoreFields_2 = (e_5 = void 0, tslib_1.__values(restoreFields)), restoreFields_2_1 = restoreFields_2.next(); !restoreFields_2_1.done; restoreFields_2_1 = restoreFields_2.next()) {
                                    var field = restoreFields_2_1.value;
                                    result[field] = article[field];
                                }
                            }
                            catch (e_5_1) { e_5 = { error: e_5_1 }; }
                            finally {
                                try {
                                    if (restoreFields_2_1 && !restoreFields_2_1.done && (_a = restoreFields_2.return)) _a.call(restoreFields_2);
                                }
                                finally { if (e_5) throw e_5.error; }
                            }
                            // remove service restored articles from articles list, so they won't be recreated
                            articles.splice(articleDataIndex, 1);
                        };
                        try {
                            for (serviceRestoreResults_1 = tslib_1.__values(serviceRestoreResults), serviceRestoreResults_1_1 = serviceRestoreResults_1.next(); !serviceRestoreResults_1_1.done; serviceRestoreResults_1_1 = serviceRestoreResults_1.next()) {
                                result = serviceRestoreResults_1_1.value;
                                _loop_1(result);
                            }
                        }
                        catch (e_4_1) { e_4 = { error: e_4_1 }; }
                        finally {
                            try {
                                if (serviceRestoreResults_1_1 && !serviceRestoreResults_1_1.done && (_d = serviceRestoreResults_1.return)) _d.call(serviceRestoreResults_1);
                            }
                            finally { if (e_4) throw e_4.error; }
                        }
                        return [3 /*break*/, 7];
                    case 6:
                        serviceRestoreResults = [];
                        _e.label = 7;
                    case 7:
                        _c = [serviceRestoreResults];
                        return [4 /*yield*/, restoreArticles(articles)];
                    case 8:
                        allRestoreResult = tslib_1.__spread.apply(void 0, _c.concat([(_e.sent())]));
                        operationResultHandler.processOperationResults(allRestoreResult);
                        return [2 /*return*/];
                }
            });
        });
    };
    var saveAsIGXB = function (options) {
        if (options === void 0) { options = {}; }
        return tslib_1.__awaiter(void 0, void 0, void 0, function () {
            var igxb, serviceInfo;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, hooks.beforeSave.emit()];
                    case 1:
                        _a.sent();
                        igxb = IGXB_1.createIGXB(articleQuery, options);
                        if (!options.useServiceFunctions) return [3 /*break*/, 3];
                        return [4 /*yield*/, saveFromServices()];
                    case 2:
                        serviceInfo = _a.sent();
                        _a.label = 3;
                    case 3: return [2 /*return*/, tslib_1.__assign(tslib_1.__assign({}, igxb), { SessionInfo: serviceInfo })];
                }
            });
        });
    };
    var restoreServices = function (data, onSourceRestoreFailure, catalogRegistrator) { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
        var sourceIds, results;
        return tslib_1.__generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    sourceIds = Object.getOwnPropertyNames(data);
                    if (!(catalogRegistrator !== undefined)) return [3 /*break*/, 2];
                    return [4 /*yield*/, catalogRegistrator.loadCatalogs(sourceIds, registry, connectionManager, localizationManager)];
                case 1:
                    _a.sent();
                    _a.label = 2;
                case 2:
                    results = [];
                    return [4 /*yield*/, Promise.all(sourceIds.map(function (sourceId) { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
                            var operator, serviceData, sourceResults, err_1;
                            return tslib_1.__generator(this, function (_a) {
                                switch (_a.label) {
                                    case 0:
                                        _a.trys.push([0, 2, , 3]);
                                        operator = serviceManager.getOperatorForSourceId(sourceId);
                                        if (!operator.is("sessionOperator")) {
                                            throw new Error("Can not restore from registered service for \"" + sourceId + "\" because its not a sessionOperator");
                                        }
                                        serviceData = data[sourceId];
                                        return [4 /*yield*/, operator.restore(serviceData)];
                                    case 1:
                                        sourceResults = _a.sent();
                                        results.push.apply(results, tslib_1.__spread(sourceResults));
                                        return [3 /*break*/, 3];
                                    case 2:
                                        err_1 = _a.sent();
                                        onSourceRestoreFailure(sourceId, err_1);
                                        return [3 /*break*/, 3];
                                    case 3: return [2 /*return*/];
                                }
                            });
                        }); }))];
                case 3:
                    _a.sent();
                    return [2 /*return*/, results];
            }
        });
    }); };
    var restoreFromServices = function (data, catalogRegistrator) { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
        var results;
        return tslib_1.__generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, restoreServices(data, function (sourceId, error) {
                        throw error;
                    }, catalogRegistrator)];
                case 1:
                    results = _a.sent();
                    return [2 /*return*/, operationResultHandler.processOperationResults(results)];
            }
        });
    }); };
    var saveFromServices = function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
        var registeredSources, serviceData;
        return tslib_1.__generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    registeredSources = serviceManager.getRegisteredSourceIds();
                    serviceData = {};
                    return [4 /*yield*/, Promise.all(registeredSources.map(function (sourceId) { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
                            var operator, _a, _b;
                            return tslib_1.__generator(this, function (_c) {
                                switch (_c.label) {
                                    case 0:
                                        operator = serviceManager.getOperatorForSourceId(sourceId);
                                        if (!operator.is("sessionOperator")) {
                                            return [2 /*return*/];
                                        }
                                        _a = serviceData;
                                        _b = sourceId;
                                        return [4 /*yield*/, operator.save()];
                                    case 1:
                                        _a[_b] = _c.sent();
                                        return [2 /*return*/];
                                }
                            });
                        }); }))];
                case 1:
                    _a.sent();
                    return [2 /*return*/, serviceData];
            }
        });
    }); };
    return {
        restoreFromArticleList: restoreFromArticleList,
        saveAsArticleList: saveAsArticleList,
        restoreFromIGXB: restoreFromIGXB,
        restoreFromServices: restoreFromServices,
        saveAsIGXB: saveAsIGXB,
        saveFromServices: function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, hooks.beforeSave.emit()];
                    case 1:
                        _a.sent();
                        return [2 /*return*/, saveFromServices()];
                }
            });
        }); },
    };
};
