/*!
 * Copyright (c) 2026 Vertex Software LLC. All rights reserved.
 */
import { ModelEntity } from "@vertexvis/scene-view-protos/core/protos/model_entity_pb";
import { MeasureRequest, ModelEntityUpdate, UpdateModelEntitiesRequest, } from "@vertexvis/scene-view-protos/sceneview/protos/scene_view_api_pb";
import { BoolValue } from "google-protobuf/google/protobuf/wrappers_pb";
import { createMetadata, requestUnary } from "../grpc";
import { mapMeasureResponseOrThrow } from "./mapper";
/**
 * The `MeasurementController` is responsible for performing measurements of
 * registered entities, and updating the model with their measurement results.
 */
export class MeasurementController {
    constructor(model, client, jwtProvider, deviceId) {
        this.model = model;
        this.client = client;
        this.jwtProvider = jwtProvider;
        this.deviceId = deviceId;
        this.outcome = Promise.resolve(undefined);
        this.debugLogs = false;
    }
    /**
     * Registers an entity to measure and performs a measurement if this entity
     * has not been previously registered.
     *
     * @param entity The entity to measure.
     * @returns A promise that resolves with the results after registering this
     * entity.
     */
    addEntity(entity) {
        if (this.debugLogs) {
            const deserializedEntity = ModelEntity.deserializeBinary(entity.modelEntity);
            this.log(`Adding ModelEntity. [entityId={${deserializedEntity.getEntityId()}}, sceneItemId={${deserializedEntity.getSceneItemId()}}]`);
        }
        return this.performMeasurement(() => this.model.addEntity(entity));
    }
    /**
     * Clears all entities and returns a promise that resolves with an empty list
     * of measurement results.
     */
    clearEntities() {
        this.log('Clearing all ModelEntities.');
        return this.performMeasurement(() => {
            this.model.clearEntities();
            this.model.clearOutcome();
            return true;
        });
    }
    /**
     * Deregisters an entity and performs a measurement if this entity was
     * removed.
     *
     * @param entity The entity to remove.
     * @returns A promise that resolves with the results after removing this
     * entity.
     */
    removeEntity(entity) {
        if (this.debugLogs) {
            const deserializedEntity = ModelEntity.deserializeBinary(entity.modelEntity);
            this.log(`Removing ModelEntity. [entityId={${deserializedEntity.getEntityId()}}, sceneItemId={${deserializedEntity.getSceneItemId()}}]`);
        }
        return this.performMeasurement(() => this.model.removeEntity(entity));
    }
    /**
     * Registers a set of entities and performs a measurement
     *
     * @param entities The entities to measure.
     * @returns A promise that resolves with the results after registering these
     * entities.
     */
    setEntities(entities) {
        if (this.debugLogs) {
            const deserializedEntities = Array.from(entities).map((e) => ModelEntity.deserializeBinary(e.modelEntity));
            this.log(`Setting ${deserializedEntities.length} ModelEntities. [entityIds=[${deserializedEntities.map((e) => e.getEntityId()).join(', ')}], sceneItemIds=[${deserializedEntities.map((e) => e.getSceneItemId()).join(', ')}]]`);
        }
        return this.performMeasurement(() => this.model.setEntities(entities));
    }
    setDebugLogs(debugLogs) {
        this.debugLogs = debugLogs;
    }
    performMeasurement(effect) {
        const previous = this.model.getEntities();
        const changed = effect();
        const entities = this.model.getEntities();
        if (changed) {
            this.measureAndUpdateModel(entities);
            this.highlightEntities(previous, entities);
        }
        return this.outcome;
    }
    measureAndUpdateModel(entities) {
        if (entities.length > 0) {
            this.outcome = this.measureEntities(entities).then((outcome) => {
                this.model.setOutcome(outcome);
                return this.model.getOutcome();
            });
        }
        else {
            this.outcome = Promise.resolve(undefined);
        }
    }
    async measureEntities(entities) {
        if (entities.length > 0) {
            const res = await requestUnary(async (handler) => {
                const meta = await createMetadata(this.jwtProvider, this.deviceId);
                const req = new MeasureRequest();
                req.setEntitiesList(entities.map((e) => e.toProto()));
                this.client.measure(req, meta, handler);
            });
            return mapMeasureResponseOrThrow(res.toObject());
        }
        else {
            return undefined;
        }
    }
    async highlightEntities(previous, entities) {
        await requestUnary(async (handler) => {
            const meta = await createMetadata(this.jwtProvider, this.deviceId);
            const entitySet = new Set(entities);
            const newPrevious = previous.filter((e) => !entitySet.has(e));
            const clearEntities = newPrevious.map((e) => {
                const update = new ModelEntityUpdate();
                update.setModelEntity(ModelEntity.deserializeBinary(e.modelEntity));
                update.setHighlight(new BoolValue().setValue(false));
                return update;
            });
            const highlightEntities = entities.map((e) => {
                const update = new ModelEntityUpdate();
                update.setModelEntity(ModelEntity.deserializeBinary(e.modelEntity));
                update.setHighlight(new BoolValue().setValue(true));
                return update;
            });
            const req = new UpdateModelEntitiesRequest();
            req.setUpdatesList([...clearEntities, ...highlightEntities]);
            this.client.updateModelEntities(req, meta, handler);
        });
    }
    log(message, ...optionalParams) {
        if (this.debugLogs) {
            console.debug(message, optionalParams);
        }
    }
}
//# sourceMappingURL=controller.js.map
