import { __assign, __read, __values } from "tslib";
import * as THREE from "@intelligentgraphics/three";
import { State as ThreeJSState, } from "@intelligentgraphics/ig.gfx.threejs";
import * as Core from "@intelligentgraphics/ig.gfx.core";
var CONTAINER_NAME = "MEASUREMENT_GROUPS_CONTAINER";
var MeasurementManager = /** @class */ (function () {
    function MeasurementManager(session, plannerManager, api, renderer, provider) {
        var _this = this;
        this.session = session;
        this.plannerManager = plannerManager;
        this.api = api;
        this.renderer = renderer;
        this.provider = provider;
        this.visible = true;
        this.targets = new Map();
        this.plannerManager.hooks.sceneUpdateDone.tap("MeasurementManager", function () {
            _this.forceUpdate();
        });
    }
    /**
     * Retrieves the current list of targets from the provider and renders the measurement again with the current visibility
     *
     */
    MeasurementManager.prototype.forceUpdate = function () {
        // for (const [identifier, target] of this.targets) {
        // 	target.dirty = true;
        // }
        this.updateTargets();
        this.render();
    };
    /**
     * Changes the visibility of the measurement of all targets.
     *
     * This won't override individually set visibilities, unless force is true.
     *
     * @param {boolean} visible
     */
    MeasurementManager.prototype.setVisibility = function (visible, force) {
        var e_1, _a;
        if (visible === this.visible && !force) {
            return;
        }
        this.visible = visible;
        // for (const [, target] of this.targets) {
        // 	target.dirty = true;
        // }
        if (force) {
            try {
                for (var _b = __values(this.targets), _c = _b.next(); !_c.done; _c = _b.next()) {
                    var _d = __read(_c.value, 2), target = _d[1];
                    target.visible = undefined;
                }
            }
            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; }
            }
        }
        this.render();
    };
    MeasurementManager.prototype.setTargetVisibility = function (identifier, visible) {
        var target = this.targets.get(identifier);
        if (target === undefined) {
            throw new Error("Can not set visibility of unknown target \"" + identifier + "\"");
        }
        if (target.visible === visible) {
            return;
        }
        target.visible = visible;
        // target.dirty = true;
        this.render();
    };
    /**
     * Removes all measurement objects from the provided scene.
     *
     * The provided scene can only be an additional scene, not the primary one.
     *
     * @param {THREE.Scene} scene
     */
    MeasurementManager.prototype.removeFromScene = function (scene) {
        if (scene === ThreeJSState.scene) {
            throw new Error("The measurement can only be removed from additional scenes, not the primary one, using this method");
        }
        var container = scene.getObjectByName(CONTAINER_NAME);
        if (container) {
            scene.remove(container);
        }
    };
    MeasurementManager.prototype.updateTargets = function () {
        var e_2, _a, e_3, _b;
        var _this = this;
        var targets = this.provider.getMeasurementTargets();
        var oldTargets = this.targets;
        this.targets = new Map();
        try {
            for (var targets_1 = __values(targets), targets_1_1 = targets_1.next(); !targets_1_1.done; targets_1_1 = targets_1.next()) {
                var target = targets_1_1.value;
                var oldTarget = oldTargets.get(target.identifier);
                var articles = void 0;
                if ("comId" in target) {
                    articles = [this.session.getArticleByComId(target.comId)];
                }
                else if ("comIds" in target) {
                    articles = target.comIds.map(function (comId) { return _this.session.getArticleByComId(comId); });
                }
                else if ("scene" in target) {
                    articles = this.session.getFilteredArticles(function (article) {
                        return article.type === "graphical" &&
                            article.gfxId !== undefined;
                    });
                }
                this.targets.set(target.identifier, __assign(__assign(__assign({ visible: undefined }, oldTarget), target), { containerId: undefined, articles: articles }));
            }
        }
        catch (e_2_1) { e_2 = { error: e_2_1 }; }
        finally {
            try {
                if (targets_1_1 && !targets_1_1.done && (_a = targets_1.return)) _a.call(targets_1);
            }
            finally { if (e_2) throw e_2.error; }
        }
        try {
            for (var oldTargets_1 = __values(oldTargets), oldTargets_1_1 = oldTargets_1.next(); !oldTargets_1_1.done; oldTargets_1_1 = oldTargets_1.next()) {
                var _c = __read(oldTargets_1_1.value, 2), target = _c[1];
                if (!this.targets.has(target.identifier)) {
                    this.deleteTarget(target);
                }
            }
        }
        catch (e_3_1) { e_3 = { error: e_3_1 }; }
        finally {
            try {
                if (oldTargets_1_1 && !oldTargets_1_1.done && (_b = oldTargets_1.return)) _b.call(oldTargets_1);
            }
            finally { if (e_3) throw e_3.error; }
        }
    };
    MeasurementManager.prototype.deleteTarget = function (target) {
        if (target.containerId === undefined) {
            return;
        }
        var groupsContainer = ThreeJSState.scene.getObjectByName(CONTAINER_NAME);
        if (!groupsContainer) {
            return;
        }
        var targetObject = groupsContainer.getObjectByName(target.containerId);
        if (targetObject) {
            groupsContainer.remove(targetObject);
        }
    };
    MeasurementManager.prototype.render = function () {
        var e_4, _a;
        var groupsContainer = ThreeJSState.scene.getObjectByName(CONTAINER_NAME);
        var targets = [];
        try {
            for (var _b = __values(this.targets), _c = _b.next(); !_c.done; _c = _b.next()) {
                var _d = __read(_c.value, 2), identifier = _d[0], target = _d[1];
                // if (!target.dirty) {
                // 	continue;
                // }
                if (!groupsContainer) {
                    groupsContainer = new THREE.Group();
                    groupsContainer.name = CONTAINER_NAME;
                    ThreeJSState.scene.add(groupsContainer);
                    if (ThreeJSState.depthShadowPlane) {
                        ThreeJSState.depthShadowPlane.setIgnored3DObject(CONTAINER_NAME, groupsContainer);
                    }
                    Core.setGlobalIgnore3DObjectInPostprocessing(CONTAINER_NAME, groupsContainer, "all");
                }
                var groupContainer = groupsContainer.getObjectByName(identifier);
                if (groupContainer === undefined) {
                    groupContainer = new THREE.Group();
                    groupContainer.name = identifier;
                    groupsContainer.add(groupContainer);
                }
                if (target.visible !== true && this.visible === false) {
                    groupContainer.visible = false;
                    continue;
                }
                groupContainer.visible = true;
                var position = void 0;
                var bounds = this.calculateGroupBounds(target.articles);
                if (target.articles.length === 1) {
                    position = this.api.GetPosition(target.articles[0].gfxId);
                }
                else {
                    position = this.calculateGroupBounds(target.articles).min;
                }
                groupContainer.position.set(position.x, position.y, position.z);
                var rotationContainer = groupContainer.children[0];
                if (rotationContainer === undefined) {
                    rotationContainer = new THREE.Object3D();
                    groupContainer.add(rotationContainer);
                }
                if (target.articles.length === 1) {
                    rotationContainer.rotation.set(0, Core.MathUtils.degToRad(this.api.GetYRotation(target.articles[0].gfxId)), 0);
                }
                else {
                    rotationContainer.rotation.set(0, 0, 0);
                }
                target.containerId = identifier;
                if ("comId" in target) {
                    targets.push({
                        article: target.articles[0],
                        parent: rotationContainer,
                        type: "article",
                        bounds: bounds,
                    });
                }
                else {
                    targets.push({
                        articles: target.articles,
                        parent: rotationContainer,
                        type: "group",
                        bounds: bounds,
                    });
                }
            }
        }
        catch (e_4_1) { e_4 = { error: e_4_1 }; }
        finally {
            try {
                if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
            }
            finally { if (e_4) throw e_4.error; }
        }
        this.renderer.renderMeasurement(targets);
        this.api.RedrawScene();
    };
    MeasurementManager.prototype.calculateGroupBounds = function (articles) {
        var e_5, _a;
        var bounds = Core.createBounds();
        try {
            for (var articles_1 = __values(articles), articles_1_1 = articles_1.next(); !articles_1_1.done; articles_1_1 = articles_1.next()) {
                var article = articles_1_1.value;
                var _b = this.api.GetBounds(article.gfxId, "World"), min = _b.min, max = _b.max;
                Core.extendBounds(bounds, min);
                Core.extendBounds(bounds, max);
            }
        }
        catch (e_5_1) { e_5 = { error: e_5_1 }; }
        finally {
            try {
                if (articles_1_1 && !articles_1_1.done && (_a = articles_1.return)) _a.call(articles_1);
            }
            finally { if (e_5) throw e_5.error; }
        }
        return bounds;
    };
    return MeasurementManager;
}());
export { MeasurementManager };
