var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import _ from "lodash";
import { distance } from "mathjs";
function hasPropertiesArray(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
obj) {
    return obj && Array.isArray(obj.properties);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function hasSingleProperty(obj) {
    return (obj && "propertyValue" in obj && typeof obj.propertyValue === "number");
}
function getUniqueInfoPickId(pick) {
    var _a;
    return `${pick.index}::${(_a = pick.layer) === null || _a === void 0 ? void 0 : _a.id}`;
}
export class MultiViewPickingInfoAssembler {
    constructor(deckGL, options = {
        multiPicking: false,
        pickDepth: 1,
    }) {
        this._deckGl = null;
        this._subscribers = new Set();
        this._deckGl = deckGL;
        this._options = options;
    }
    setDeckGL(deckGL) {
        this._deckGl = deckGL;
    }
    subscribe(callback) {
        this._subscribers.add(callback);
        return () => {
            this._subscribers.delete(callback);
        };
    }
    publish(info, activeViewportId) {
        for (const subscriber of this._subscribers) {
            subscriber(info, activeViewportId);
        }
    }
    getMultiViewPickingInfo(hoverEvent) {
        var _a, _b, _c;
        if (!((_a = this._deckGl) === null || _a === void 0 ? void 0 : _a.deck)) {
            return;
        }
        const viewports = (_b = this._deckGl.deck) === null || _b === void 0 ? void 0 : _b.getViewports();
        if (!viewports) {
            return;
        }
        if (hoverEvent.infos.length === 0) {
            return;
        }
        const activeViewportId = (_c = hoverEvent.infos[0].viewport) === null || _c === void 0 ? void 0 : _c.id;
        if (!activeViewportId) {
            return;
        }
        const eventScreenCoordinate = [
            hoverEvent.infos[0].x,
            hoverEvent.infos[0].y,
        ];
        this.assembleMultiViewPickingInfo(eventScreenCoordinate, activeViewportId, viewports).then((info) => {
            this.publish(info, activeViewportId);
        });
    }
    pickAtCoordinate(x, y) {
        var _a;
        const deck = (_a = this._deckGl) === null || _a === void 0 ? void 0 : _a.deck;
        if (!deck)
            return [];
        if (this._options.multiPicking) {
            // ! For some reason, multi-pick pads the array up to pick-depth length, repeating the last element
            const multPickResult = deck.pickMultipleObjects({
                depth: this._options.pickDepth,
                unproject3D: true,
                x,
                y,
            });
            // Ensure the top-most element is processed first by sorting based on distance to camera
            return _.sortBy(multPickResult, (pick) => {
                var _a;
                if (!((_a = pick.viewport) === null || _a === void 0 ? void 0 : _a.cameraPosition))
                    return -1;
                if (!pick.coordinate)
                    return -1;
                return distance(pick.coordinate, pick.viewport.cameraPosition);
            });
        }
        else {
            const obj = deck.pickObject({
                unproject3D: true,
                x,
                y,
            });
            return obj ? [obj] : [];
        }
    }
    assembleMultiViewPickingInfo(eventScreenCoordinate, activeViewportId, viewports) {
        return __awaiter(this, void 0, void 0, function* () {
            return new Promise((resolve, reject) => {
                var _a, _b, _c;
                if (!((_a = this._deckGl) === null || _a === void 0 ? void 0 : _a.deck)) {
                    reject("DeckGL not initialized");
                    return;
                }
                const activeViewport = viewports.find((el) => el.id === activeViewportId);
                if (!activeViewport) {
                    reject("Active viewport not found");
                    return;
                }
                const activeViewportRelativeScreenCoordinates = [
                    eventScreenCoordinate[0] - activeViewport.x,
                    eventScreenCoordinate[1] - activeViewport.y,
                ];
                const activePickingInfo = this.pickAtCoordinate(eventScreenCoordinate[0], eventScreenCoordinate[1]);
                const worldCoordinate = (_c = (_b = activePickingInfo[0]) === null || _b === void 0 ? void 0 : _b.coordinate) !== null && _c !== void 0 ? _c : activeViewport.unproject(activeViewportRelativeScreenCoordinates);
                const collectedPickingInfo = {};
                for (const viewport of viewports) {
                    const [relativeScreenX, relativeScreenY] = viewport.project(worldCoordinate);
                    const pickingInfo = this.pickAtCoordinate(relativeScreenX + viewport.x, relativeScreenY + viewport.y);
                    if (pickingInfo) {
                        const layerInfoDict = {};
                        const processedPickIds = [];
                        for (const info of pickingInfo) {
                            const uniquePickId = getUniqueInfoPickId(info);
                            const hasMultipleProperties = hasPropertiesArray(info);
                            const hasOneProperty = hasSingleProperty(info);
                            // General guard clauses
                            if (!info.layer)
                                continue;
                            if (processedPickIds.includes(uniquePickId))
                                continue;
                            if (!hasMultipleProperties && !hasOneProperty)
                                continue;
                            processedPickIds.push(uniquePickId);
                            const layerId = info.layer.id;
                            const layerProps = info.layer
                                .props;
                            const layerName = layerProps.name;
                            if (!layerInfoDict[layerId]) {
                                layerInfoDict[layerId] = {
                                    layerId,
                                    layerName,
                                    properties: [],
                                };
                            }
                            const layerPickingInfo = layerInfoDict[layerId];
                            if (hasOneProperty) {
                                layerPickingInfo.properties.push({
                                    name: "Value",
                                    value: info.propertyValue,
                                    color: undefined,
                                });
                            }
                            else if (hasMultipleProperties) {
                                const properties = info.properties;
                                for (const property of properties) {
                                    layerPickingInfo.properties.push({
                                        name: property.name,
                                        value: property.value,
                                        color: property.color,
                                    });
                                }
                            }
                        }
                        collectedPickingInfo[viewport.id] = {
                            coordinates: worldCoordinate,
                            layerPickingInfo: Object.values(layerInfoDict),
                        };
                    }
                    else {
                        collectedPickingInfo[viewport.id] = {
                            coordinates: worldCoordinate,
                            layerPickingInfo: [],
                        };
                    }
                }
                resolve(collectedPickingInfo);
            });
        });
    }
}
//# sourceMappingURL=MultiViewPickingInfoAssembler.js.map