"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var ig_gfx_core_1 = require("@intelligentgraphics/ig.gfx.core");
var ig_gfx_planning_1 = require("@intelligentgraphics/ig.gfx.planning");
var ig_gfx_multiarticle_1 = require("@intelligentgraphics/ig.gfx.multiarticle");
var ig_utilities_1 = require("@intelligentgraphics/ig.utilities");
var Logger_1 = require("./Logger");
var MultiArticleExecutor_1 = require("./MultiArticleExecutor");
var Logger = Logger_1.default.createSubLogger("GhostModeManager");
var AttachPointsLogger = Logger.createSubLogger("AttachPoints");
var GhostModeManager = /** @class */ (function () {
    function GhostModeManager(session, plannerManager, options) {
        var _this = this;
        if (options === void 0) { options = {}; }
        this.session = session;
        this.targets = new Map();
        this.articleActions = new Map();
        /**
         * Creates a createCopy method for the ghost mode based on an article catalog item
         *
         * @private
         */
        this.createArticleCopyFactory = function (articleId) { return function (transformation, actionRules, snapTo) {
            if (actionRules === void 0) { actionRules = []; }
            return tslib_1.__awaiter(_this, void 0, void 0, function () {
                var configuration, converter, snapPos, ruleParams, actionRules_1, actionRules_1_1, rule, action, rootGfxId, article, comId;
                var e_1, _a;
                return tslib_1.__generator(this, function (_b) {
                    switch (_b.label) {
                        case 0:
                            if (actionRules && actionRules.length && snapTo !== undefined) {
                                converter = new ig_gfx_planning_1.WorldLocalConverter(snapTo);
                                snapPos = converter.toLocal(transformation.position);
                                ruleParams = Object.assign({}, ig_gfx_multiarticle_1.Rule.createAdditionalRuleParameters({
                                    context: snapTo,
                                    hasBottomNeighbor: false,
                                    hasTopNeighbor: false,
                                    hasLeftNeighbor: false,
                                    hasRightNeighbor: false,
                                    relativeSnapPosition: snapPos,
                                }), this.componentConfiguration);
                                configuration = {};
                                try {
                                    for (actionRules_1 = tslib_1.__values(actionRules), actionRules_1_1 = actionRules_1.next(); !actionRules_1_1.done; actionRules_1_1 = actionRules_1.next()) {
                                        rule = actionRules_1_1.value;
                                        action = ig_gfx_multiarticle_1.Rule.evalActionRule(rule, ruleParams);
                                        if (action.isActive && action.property !== undefined) {
                                            configuration[action.property] = action.value;
                                        }
                                    }
                                }
                                catch (e_1_1) { e_1 = { error: e_1_1 }; }
                                finally {
                                    try {
                                        if (actionRules_1_1 && !actionRules_1_1.done && (_a = actionRules_1.return)) _a.call(actionRules_1);
                                    }
                                    finally { if (e_1) throw e_1.error; }
                                }
                            }
                            Logger.info("Creating a new article", {
                                articleId: articleId,
                                transformation: transformation,
                                configuration: configuration,
                            });
                            if (snapTo && this.options.applySnapToConfiguration) {
                                rootGfxId = ig_gfx_core_1.Hierarchy.getRootGfxId(snapTo);
                                article = this.session.getArticleByGfxId(rootGfxId);
                                if (article !== undefined) {
                                    configuration = tslib_1.__assign(tslib_1.__assign({}, article.configuration), configuration);
                                }
                            }
                            return [4 /*yield*/, this.createArticle(this.templateArticle.source, articleId, configuration, {
                                    position: transformation.position,
                                    rotation: {
                                        x: ig_gfx_core_1.MathUtils.radToDeg(transformation.rotation.x),
                                        y: ig_gfx_core_1.MathUtils.radToDeg(transformation.rotation.y),
                                        z: ig_gfx_core_1.MathUtils.radToDeg(transformation.rotation.z),
                                    },
                                })];
                        case 1:
                            comId = _b.sent();
                            this.articleActions.set(comId, {
                                actionRules: actionRules,
                                snapTo: snapTo,
                                comId: comId,
                            });
                            return [2 /*return*/];
                    }
                });
            });
        }; };
        /**
         * Called after a ghost mode has been stopped
         *
         * @private
         */
        this.cleanupGhostMode = function () {
            if (_this.ghostInsertModeInstance) {
                _this.ghostInsertModeInstance = undefined;
            }
            _this.componentConfiguration = undefined;
            _this.onGhostModeEnd();
            if (_this.templateArticle) {
                _this.removeArticle(_this.templateArticle.comId);
            }
        };
        this.options = {
            applySnapToConfiguration: ig_utilities_1.valueOrDefault(options.applySnapToConfiguration, true),
        };
        plannerManager.hooks.prepareForUpdate.tap("GhostInsertMode", function (article) {
            return _this.onPrepareForUpdate(article);
        });
        plannerManager.hooks.articleUpdateDone.tap("GhostInsertMode", function (article) {
            return _this.onUpdateDone(article);
        });
        plannerManager.hooks.sceneUpdateDone.tap("GhostInsertMode", function () {
            return _this.executeArticleActions();
        });
    }
    GhostModeManager.prototype.getTemplateArticleInstance = function () {
        return this.templateArticle;
    };
    GhostModeManager.prototype.stop = function () {
        if (this.ghostInsertModeInstance) {
            this.ghostInsertModeInstance.stop();
        }
    };
    GhostModeManager.prototype.createArticle = function (source, articleId, initialConfiguration, initialPlacement) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var catalogManager, articleItem;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        catalogManager = this.session.getCatalogManagerForSourceId(source);
                        if (catalogManager === undefined) {
                            throw new Error("The default implementation of GhostModeManager.createArticle requires a catalog manager to be registered for the specified sourceId");
                        }
                        return [4 /*yield*/, catalogManager.getArticle(articleId)];
                    case 1:
                        articleItem = _a.sent();
                        if (articleItem === undefined) {
                            throw new Error("Could not fetch catalog article item from id " + articleId);
                        }
                        return [2 /*return*/, this.session.createArticleFromCatalogItem({
                                source: source,
                                catalogItem: articleItem,
                                initialConfiguration: initialConfiguration,
                                initialPlacement: initialPlacement,
                            })];
                }
            });
        });
    };
    GhostModeManager.prototype.removeArticle = function (comId) {
        this.session.setArticleGfxId(comId, undefined);
        return this.session.deleteArticle(comId);
    };
    GhostModeManager.prototype.onGhostModeStart = function () { };
    GhostModeManager.prototype.onGhostModeEnd = function () { };
    GhostModeManager.prototype.executeArticleActions = function () {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var executionPromises, _loop_1, this_1, _a, _b, _c, comId, actionConfig;
            var e_2, _d;
            return tslib_1.__generator(this, function (_e) {
                switch (_e.label) {
                    case 0:
                        executionPromises = [];
                        _loop_1 = function (comId, actionConfig) {
                            // article has been created through the ghostmode and actions have to be executed
                            var rootId = this_1.session.getArticleGfxId(comId);
                            if (rootId === undefined) {
                                return "continue";
                            }
                            this_1.articleActions.delete(comId);
                            executionPromises.push(ig_gfx_multiarticle_1.Rule.executeActions(actionConfig.actionRules, rootId, actionConfig.snapTo).catch(function () {
                                return Logger.error("Could not execute actions from GhostInsertMode for " + comId);
                            }));
                        };
                        this_1 = this;
                        try {
                            for (_a = tslib_1.__values(this.articleActions), _b = _a.next(); !_b.done; _b = _a.next()) {
                                _c = tslib_1.__read(_b.value, 2), comId = _c[0], actionConfig = _c[1];
                                _loop_1(comId, actionConfig);
                            }
                        }
                        catch (e_2_1) { e_2 = { error: e_2_1 }; }
                        finally {
                            try {
                                if (_b && !_b.done && (_d = _a.return)) _d.call(_a);
                            }
                            finally { if (e_2) throw e_2.error; }
                        }
                        return [4 /*yield*/, Promise.all(executionPromises)];
                    case 1:
                        _e.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    GhostModeManager.prototype.onPrepareForUpdate = function (article) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            return tslib_1.__generator(this, function (_a) {
                if (!article.placement || !article.placement.position) {
                    // article was not yet placed -> is not being restored or changed
                    this.targets.set(article.comId, article);
                    return [2 /*return*/];
                }
                return [2 /*return*/];
            });
        });
    };
    GhostModeManager.prototype.onUpdateDone = function (article) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var isTarget;
            return tslib_1.__generator(this, function (_a) {
                isTarget = this.targets.has(article.comId);
                this.targets.delete(article.comId);
                // if a ghostMode is already running -> refresh
                if (this.ghostInsertModeInstance) {
                    if (article.articleNumber !== undefined &&
                        this.templateCatalogItemId ===
                            article.articleNumber.baseArticleNumber) {
                        try {
                            Logger.info("Refreshing ghost mode after article insertion");
                            this.ghostInsertModeInstance.refreshGhosts(); //redraws the still available ghosts (might be one less)
                        }
                        catch (err) {
                            Logger.error("Could not refresh ghosts after update", err);
                        }
                        return [2 /*return*/];
                    }
                    this.ghostInsertModeInstance.stop();
                }
                // if the supplied article argument has not been set as a potential target -> ignore
                if (!isTarget || !article.gfxId) {
                    return [2 /*return*/];
                }
                // check if ghost mode can be applied to object -> start ghost mode
                if (ig_gfx_planning_1.GhostInsertMode.isGhostModeNecessary(article.gfxId, MultiArticleExecutor_1.findMultiArticleConfiguration(this.session, article.comId))) {
                    AttachPointsLogger.info("Starting ghost mode for " + article.comId);
                    return [2 /*return*/, this.triggerGhostMode(article)];
                }
                AttachPointsLogger.info("Not starting ghost mode for " + article.comId + " because no points are available");
                return [2 /*return*/];
            });
        });
    };
    GhostModeManager.prototype.triggerGhostMode = function (article) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var articleNumber, gfxId, _a;
            return tslib_1.__generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        // save template article, so it can be deleted later
                        this.templateArticle = article;
                        articleNumber = article.articleNumber, gfxId = article.gfxId;
                        this.templateCatalogItemId =
                            articleNumber !== undefined
                                ? articleNumber.baseArticleNumber
                                : article.articleId;
                        _a = this;
                        return [4 /*yield*/, MultiArticleExecutor_1.findMultiArticleConfiguration(this.session, article.comId)];
                    case 1:
                        _a.componentConfiguration = _b.sent();
                        this.ghostInsertModeInstance = new ig_gfx_planning_1.GhostInsertMode(gfxId, this.createArticleCopyFactory(this.templateCatalogItemId), this.cleanupGhostMode, this.componentConfiguration);
                        this.ghostInsertModeInstance.start();
                        this.onGhostModeStart();
                        return [2 /*return*/];
                }
            });
        });
    };
    return GhostModeManager;
}());
exports.GhostModeManager = GhostModeManager;
