/*!
 * Copyright (c) 2026 Vertex Software LLC. All rights reserved.
 */
import { Point } from "@vertexvis/geometry";
import { UUID } from "@vertexvis/utils";
import { translatePointToRelative } from "../../components/viewer-pin-tool/utils";
import { labelPinCursor, pinCursor } from "../cursors";
import { getMouseClientPosition } from "../dom";
import { ElementRectObserver } from "../elementRectObserver";
import { EntityType } from "../types/entities";
export class PinsInteractionHandler {
    get elementRect() {
        return this.rectObserver.rect;
    }
    constructor(controller, xOffset, yOffset) {
        this.rectObserver = new ElementRectObserver();
        this.droppableSurfaces = [
            EntityType.PRECISE_SURFACE,
            EntityType.IMPRECISE_SURFACE,
            EntityType.GENERIC_GEOMETRY,
        ];
        this.handlePointerMove = async (event) => {
            const isDroppableSurface = await this.isDroppableSurface(event);
            const draggable = this.controller.getDraggable();
            if (draggable != null && isDroppableSurface) {
                this.handleDrag(draggable, event);
            }
            if (this.controller.getDraggable() == null &&
                this.controller.getToolMode() === 'edit' &&
                isDroppableSurface) {
                this.addCursor(this.getCursorType());
            }
            else {
                this.clearCursor();
            }
        };
        this.handleDrag = async (draggable, event) => {
            const pt = getMouseClientPosition(event, this.elementRect);
            const worldPosition = await this.getWorldPositionForPoint(pt);
            if (worldPosition != null) {
                this.controller.updateDraggable({
                    ...draggable,
                    lastPoint: pt,
                }, worldPosition);
            }
        };
        this.handlePointerDown = async (pointerDown) => {
            const pointerUp = (pointerUp) => {
                const distanceBetweenStartAndEndPoint = Point.distance(Point.create(pointerDown.clientX, pointerUp.clientY), Point.create(pointerUp.clientX, pointerUp.clientY));
                if (distanceBetweenStartAndEndPoint <= 2 && pointerDown.buttons !== 2) {
                    if (this.controller.getToolMode() === 'edit') {
                        const pt = getMouseClientPosition(pointerDown, this.elementRect);
                        this.handlePlacePin(pt);
                    }
                }
                dispose();
            };
            const dispose = () => {
                window.removeEventListener('pointerup', pointerUp);
            };
            window.addEventListener('pointerup', pointerUp);
            return {
                dispose,
            };
        };
        this.handlePointerUp = async () => {
            const draggable = this.controller.getDraggable();
            const lastPoint = draggable === null || draggable === void 0 ? void 0 : draggable.lastPoint;
            this.controller.setDraggable(undefined);
            if (lastPoint != null && draggable != null) {
                this.ifInitialized(async ({ api }) => {
                    var _a, _b, _c, _d, _e, _f;
                    const [hit] = await api.hitItems(lastPoint);
                    if ((hit === null || hit === void 0 ? void 0 : hit.hitPoint) != null &&
                        ((_a = hit === null || hit === void 0 ? void 0 : hit.hitPoint) === null || _a === void 0 ? void 0 : _a.x) != null &&
                        ((_b = hit === null || hit === void 0 ? void 0 : hit.hitPoint) === null || _b === void 0 ? void 0 : _b.y) != null &&
                        ((_c = hit === null || hit === void 0 ? void 0 : hit.hitPoint) === null || _c === void 0 ? void 0 : _c.z) != null) {
                        this.controller.updateDraggable({
                            ...draggable,
                            lastPoint,
                        }, {
                            x: (_d = hit === null || hit === void 0 ? void 0 : hit.hitPoint) === null || _d === void 0 ? void 0 : _d.x,
                            y: hit === null || hit === void 0 ? void 0 : hit.hitPoint.y,
                            z: hit === null || hit === void 0 ? void 0 : hit.hitPoint.z,
                        }, (_f = (_e = hit === null || hit === void 0 ? void 0 : hit.partId) === null || _e === void 0 ? void 0 : _e.hex) !== null && _f !== void 0 ? _f : undefined);
                    }
                });
            }
        };
        this.controller = controller;
        this.xOffset = xOffset;
        this.yOffset = yOffset;
    }
    initialize(element, api) {
        this.element = element;
        this.api = api;
        this.rectObserver.observe(element);
        element.addEventListener('pointermove', this.handlePointerMove);
        element.addEventListener('pointerdown', this.handlePointerDown);
        window.addEventListener('pointerup', this.handlePointerUp);
    }
    dispose() {
        var _a, _b;
        this.rectObserver.disconnect();
        this.clearCursor();
        (_a = this.element) === null || _a === void 0 ? void 0 : _a.removeEventListener('pointermove', this.handlePointerMove);
        (_b = this.element) === null || _b === void 0 ? void 0 : _b.removeEventListener('pointerdown', this.handlePointerDown);
        window.removeEventListener('pointerup', this.handlePointerUp);
        this.element = undefined;
        this.api = undefined;
    }
    async getWorldPositionForPoint(pt) {
        return this.ifInitialized(async ({ api }) => {
            const vector3 = await api.getWorldPointFromViewport(pt);
            return vector3;
        });
    }
    async handlePlacePin(pt, existingPin) {
        this.ifInitialized(async ({ api }) => {
            var _a, _b, _c, _d, _e, _f;
            const [hit] = await api.hitItems(pt);
            if ((hit === null || hit === void 0 ? void 0 : hit.hitPoint) != null && this.elementRect != null) {
                if ((hit === null || hit === void 0 ? void 0 : hit.hitPoint) != null &&
                    (hit === null || hit === void 0 ? void 0 : hit.hitPoint.x) != null &&
                    (hit === null || hit === void 0 ? void 0 : hit.hitPoint.y) != null &&
                    (hit === null || hit === void 0 ? void 0 : hit.hitPoint.z) != null) {
                    const pinId = existingPin != null ? existingPin.id : UUID.create();
                    const attributes = this.controller.getStyleAttributes() != null
                        ? {
                            style: this.controller.getStyleAttributes(),
                        }
                        : undefined;
                    switch (this.controller.getToolType()) {
                        case 'pin-icon':
                            this.controller.setPin({
                                type: 'icon',
                                id: pinId,
                                worldPosition: {
                                    x: (_a = hit === null || hit === void 0 ? void 0 : hit.hitPoint) === null || _a === void 0 ? void 0 : _a.x,
                                    y: hit === null || hit === void 0 ? void 0 : hit.hitPoint.y,
                                    z: hit === null || hit === void 0 ? void 0 : hit.hitPoint.z,
                                },
                                partId: (_c = (_b = hit === null || hit === void 0 ? void 0 : hit.itemId) === null || _b === void 0 ? void 0 : _b.hex) !== null && _c !== void 0 ? _c : undefined,
                                attributes,
                            });
                            break;
                        case 'pin-text':
                            const isNewPin = existingPin == null;
                            const relativePoint = translatePointToRelative(pt, this.elementRect, isNewPin, this.xOffset, this.yOffset);
                            this.controller.setPin({
                                type: 'text',
                                id: pinId,
                                worldPosition: {
                                    x: (_d = hit === null || hit === void 0 ? void 0 : hit.hitPoint) === null || _d === void 0 ? void 0 : _d.x,
                                    y: hit === null || hit === void 0 ? void 0 : hit.hitPoint.y,
                                    z: hit === null || hit === void 0 ? void 0 : hit.hitPoint.z,
                                },
                                partId: (_f = (_e = hit === null || hit === void 0 ? void 0 : hit.itemId) === null || _e === void 0 ? void 0 : _e.hex) !== null && _f !== void 0 ? _f : undefined,
                                label: {
                                    point: relativePoint,
                                },
                                attributes,
                            });
                            this.controller.setSelectedPinId(pinId);
                            break;
                    }
                }
            }
            else {
                this.controller.setSelectedPinId();
            }
        });
    }
    getCursorType() {
        switch (this.controller.getToolType()) {
            case 'pin-icon':
                return pinCursor;
            case 'pin-text':
                return labelPinCursor;
        }
    }
    addCursor(cursor) {
        this.clearCursor();
        this.cursor = this.ifInitialized(({ api }) => api.addCursor(cursor));
    }
    async isDroppableSurface(event) {
        var _a;
        const pt = getMouseClientPosition(event, this.elementRect);
        const type = await ((_a = this.api) === null || _a === void 0 ? void 0 : _a.getEntityTypeAtPoint(pt));
        return type != null && this.droppableSurfaces.includes(type);
    }
    ifInitialized(f) {
        if (this.element != null && this.api != null) {
            return f({ element: this.element, api: this.api });
        }
        else {
            throw new Error('Pins interaction handler not initialized.');
        }
    }
    clearCursor() {
        var _a;
        (_a = this.cursor) === null || _a === void 0 ? void 0 : _a.dispose();
        this.cursor = undefined;
    }
}
//# sourceMappingURL=interactions.js.map
