import { vec2 } from 'gl-matrix';
import { Events } from '../../enums';
import { getEnabledElement, utilities as csUtils, getEnabledElementByViewportId, } from '@cornerstonejs/core';
import { AnnotationTool } from '../base';
import { addAnnotation, getAnnotations, removeAnnotation, } from '../../stateManagement/annotation/annotationState';
import { drawTextBox as drawTextBoxSvg } from '../../drawingSvg';
import { state } from '../../store/state';
import { getViewportIdsWithToolToRender } from '../../utilities/viewportFilters';
import triggerAnnotationRenderForViewportIds from '../../utilities/triggerAnnotationRenderForViewportIds';
import { triggerAnnotationCompleted, triggerAnnotationModified, } from '../../stateManagement/annotation/helpers/state';
import { resetElementCursor, hideElementCursor, } from '../../cursors/elementCursor';
class LabelTool extends AnnotationTool {
    constructor(toolProps = {}, defaultToolProps = {
        supportedInteractionTypes: ['Mouse', 'Touch'],
        configuration: {
            shadow: true,
            getTextCallback,
            changeTextCallback,
            preventHandleOutsideImage: false,
        },
    }) {
        super(toolProps, defaultToolProps);
        this.isPointNearTool = (element, annotation, canvasCoords, proximity) => {
            const enabledElement = getEnabledElement(element);
            const { viewport } = enabledElement;
            const { annotationUID } = annotation;
            const point = annotation.data.handles.points[0];
            const annotationCanvasCoordinate = viewport.worldToCanvas(point);
            const dist = vec2.distance(canvasCoords, annotationCanvasCoordinate);
            if (dist < proximity) {
                return true;
            }
            const svgLayer = element.querySelector('svg');
            if (!svgLayer) {
                return false;
            }
            const textGroup = svgLayer.querySelector(`g[data-annotation-uid="${annotationUID}"]`);
            if (!textGroup) {
                return false;
            }
            const textGroupElement = textGroup;
            const bbox = textGroupElement.getBBox();
            const transform = textGroupElement.getAttribute('transform');
            let translateX = 0;
            let translateY = 0;
            if (transform) {
                const matches = transform.match(/translate\(([-\d.]+)\s+([-\d.]+)\)/);
                if (matches) {
                    translateX = parseFloat(matches[1]);
                    translateY = parseFloat(matches[2]);
                }
            }
            const x = bbox.x + translateX;
            const y = bbox.y + translateY;
            const isNear = canvasCoords[0] >= x &&
                canvasCoords[0] <= x + bbox.width &&
                canvasCoords[1] >= y &&
                canvasCoords[1] <= y + bbox.height;
            return isNear;
        };
        this.addNewAnnotation = (evt) => {
            const eventDetail = evt.detail;
            const { currentPoints, element } = eventDetail;
            const worldPos = currentPoints.world;
            const enabledElement = getEnabledElement(element);
            const { viewport } = enabledElement;
            hideElementCursor(element);
            this.isDrawing = true;
            const camera = viewport.getCamera();
            const { viewPlaneNormal, viewUp } = camera;
            const referencedImageId = this.getReferencedImageId(viewport, worldPos, viewPlaneNormal, viewUp);
            const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
            const annotation = {
                annotationUID: null,
                highlighted: true,
                invalidated: true,
                metadata: {
                    toolName: this.getToolName(),
                    viewPlaneNormal: [...viewPlaneNormal],
                    viewUp: [...viewUp],
                    FrameOfReferenceUID,
                    referencedImageId,
                    ...viewport.getViewReference({ points: [worldPos] }),
                },
                data: {
                    text: '',
                    handles: {
                        points: [[...worldPos], [...worldPos]],
                    },
                    label: '',
                },
            };
            addAnnotation(annotation, element);
            const viewportIdsToRender = getViewportIdsWithToolToRender(element, this.getToolName());
            evt.preventDefault();
            triggerAnnotationRenderForViewportIds(viewportIdsToRender);
            this.configuration.getTextCallback((text) => {
                if (!text) {
                    removeAnnotation(annotation.annotationUID);
                    triggerAnnotationRenderForViewportIds(viewportIdsToRender);
                    this.isDrawing = false;
                    return;
                }
                resetElementCursor(element);
                annotation.data.text = text;
                triggerAnnotationCompleted(annotation);
                triggerAnnotationRenderForViewportIds(viewportIdsToRender);
            });
            this.createMemo(element, annotation, { newAnnotation: true });
            return annotation;
        };
        this.toolSelectedCallback = (evt, annotation) => {
            const eventDetail = evt.detail;
            const { element, currentPoints } = eventDetail;
            annotation.highlighted = true;
            const viewportIdsToRender = getViewportIdsWithToolToRender(element, this.getToolName());
            let offset = [0, 0, 0];
            if (currentPoints && currentPoints.world) {
                const initialWorldPos = currentPoints.world;
                const anchorWorldPos = annotation.data.handles.points[0];
                offset = [
                    anchorWorldPos[0] - initialWorldPos[0],
                    anchorWorldPos[1] - initialWorldPos[1],
                    anchorWorldPos[2] - initialWorldPos[2],
                ];
            }
            this.editData = {
                annotation,
                viewportIdsToRender,
                offset,
            };
            this._activateModify(element);
            hideElementCursor(element);
            triggerAnnotationRenderForViewportIds(viewportIdsToRender);
            evt.preventDefault();
        };
        this._endCallback = (evt) => {
            const eventDetail = evt.detail;
            const { element } = eventDetail;
            const { annotation, viewportIdsToRender, newAnnotation } = this.editData;
            this._deactivateDraw(element);
            this._deactivateModify(element);
            resetElementCursor(element);
            if (newAnnotation) {
                this.createMemo(element, annotation, { newAnnotation });
            }
            this.editData = null;
            this.isDrawing = false;
            this.doneEditMemo();
            if (this.isHandleOutsideImage &&
                this.configuration.preventHandleOutsideImage) {
                removeAnnotation(annotation.annotationUID);
            }
            triggerAnnotationRenderForViewportIds(viewportIdsToRender);
            if (newAnnotation) {
                triggerAnnotationCompleted(annotation);
            }
        };
        this._dragCallback = (evt) => {
            const eventDetail = evt.detail;
            const { currentPoints } = eventDetail;
            const worldPos = currentPoints.world;
            const { annotation, viewportIdsToRender, offset } = this.editData;
            if (offset) {
                annotation.data.handles.points[0] = [
                    worldPos[0] + offset[0],
                    worldPos[1] + offset[1],
                    worldPos[2] + offset[2],
                ];
            }
            else {
                annotation.data.handles.points[0] = [...worldPos];
            }
            annotation.invalidated = true;
            triggerAnnotationRenderForViewportIds(viewportIdsToRender);
        };
        this.cancel = (element) => {
            if (this.isDrawing) {
                this.isDrawing = false;
                this._deactivateModify(element);
                resetElementCursor(element);
                const { annotation, viewportIdsToRender, newAnnotation } = this.editData;
                const { data } = annotation;
                annotation.highlighted = false;
                data.handles.activeHandleIndex = null;
                triggerAnnotationRenderForViewportIds(viewportIdsToRender);
                if (newAnnotation) {
                    triggerAnnotationCompleted(annotation);
                }
                this.editData = null;
                return annotation.annotationUID;
            }
        };
        this._activateModify = (element) => {
            state.isInteractingWithTool = true;
            element.addEventListener(Events.MOUSE_UP, this._endCallback);
            element.addEventListener(Events.MOUSE_DRAG, this._dragCallback);
            element.addEventListener(Events.MOUSE_CLICK, this._endCallback);
            element.addEventListener(Events.TOUCH_TAP, this._endCallback);
            element.addEventListener(Events.TOUCH_END, this._endCallback);
            element.addEventListener(Events.TOUCH_DRAG, this._dragCallback);
        };
        this._deactivateModify = (element) => {
            state.isInteractingWithTool = false;
            element.removeEventListener(Events.MOUSE_UP, this._endCallback);
            element.removeEventListener(Events.MOUSE_DRAG, this._dragCallback);
            element.removeEventListener(Events.MOUSE_CLICK, this._endCallback);
            element.removeEventListener(Events.TOUCH_TAP, this._endCallback);
            element.removeEventListener(Events.TOUCH_DRAG, this._dragCallback);
            element.removeEventListener(Events.TOUCH_END, this._endCallback);
        };
        this._activateDraw = (element) => {
            state.isInteractingWithTool = true;
            element.addEventListener(Events.MOUSE_UP, this._endCallback);
            element.addEventListener(Events.MOUSE_DRAG, this._dragCallback);
            element.addEventListener(Events.MOUSE_MOVE, this._dragCallback);
            element.addEventListener(Events.MOUSE_CLICK, this._endCallback);
            element.addEventListener(Events.TOUCH_TAP, this._endCallback);
            element.addEventListener(Events.TOUCH_END, this._endCallback);
            element.addEventListener(Events.TOUCH_DRAG, this._dragCallback);
        };
        this._deactivateDraw = (element) => {
            state.isInteractingWithTool = false;
            element.removeEventListener(Events.MOUSE_UP, this._endCallback);
            element.removeEventListener(Events.MOUSE_DRAG, this._dragCallback);
            element.removeEventListener(Events.MOUSE_MOVE, this._dragCallback);
            element.removeEventListener(Events.MOUSE_CLICK, this._endCallback);
            element.removeEventListener(Events.TOUCH_TAP, this._endCallback);
            element.removeEventListener(Events.TOUCH_END, this._endCallback);
            element.removeEventListener(Events.TOUCH_DRAG, this._dragCallback);
        };
        this.renderAnnotation = (enabledElement, svgDrawingHelper) => {
            let renderStatus = false;
            const { viewport } = enabledElement;
            const { element } = viewport;
            let annotations = getAnnotations(this.getToolName(), element);
            if (!annotations?.length) {
                return renderStatus;
            }
            annotations = this.filterInteractableAnnotationsForElement(element, annotations);
            const styleSpecifier = {
                toolGroupId: this.toolGroupId,
                toolName: this.getToolName(),
                viewportId: enabledElement.viewport.id,
            };
            for (let i = 0; i < annotations.length; i++) {
                const annotation = annotations[i];
                const { annotationUID, data } = annotation;
                const point = data.handles.points[0];
                styleSpecifier.annotationUID = annotationUID;
                const canvasCoordinates = viewport.worldToCanvas(point);
                renderStatus = true;
                if (!viewport.getRenderingEngine()) {
                    console.warn('Rendering Engine has been destroyed');
                    return renderStatus;
                }
                if (!data.text) {
                    continue;
                }
                const options = this.getLinkedTextBoxStyle(styleSpecifier, annotation);
                const textBoxUID = '1';
                drawTextBoxSvg(svgDrawingHelper, annotationUID, textBoxUID, [data.text], canvasCoordinates, {
                    ...options,
                    padding: 0,
                });
            }
            return renderStatus;
        };
    }
    static { this.hydrate = (viewportId, position, text, options) => {
        const enabledElement = getEnabledElementByViewportId(viewportId);
        if (!enabledElement) {
            return;
        }
        const { viewport } = enabledElement;
        const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
        const { viewPlaneNormal, viewUp } = viewport.getCamera();
        const instance = new this();
        const referencedImageId = instance.getReferencedImageId(viewport, position, viewPlaneNormal, viewUp);
        const annotation = {
            annotationUID: options?.annotationUID || csUtils.uuidv4(),
            data: {
                text,
                handles: {
                    points: [position],
                },
            },
            highlighted: false,
            autoGenerated: false,
            invalidated: false,
            isLocked: false,
            isVisible: true,
            metadata: {
                toolName: instance.getToolName(),
                viewPlaneNormal,
                FrameOfReferenceUID,
                referencedImageId,
                ...options,
            },
        };
        addAnnotation(annotation, viewport.element);
        triggerAnnotationRenderForViewportIds([viewport.id]);
    }; }
    handleSelectedCallback(evt, annotation, handle, interactionType) { }
    _doneChangingTextCallback(element, annotation, updatedText) {
        annotation.data.text = updatedText;
        const viewportIdsToRender = getViewportIdsWithToolToRender(element, this.getToolName());
        triggerAnnotationRenderForViewportIds(viewportIdsToRender);
        triggerAnnotationModified(annotation, element);
    }
    _isInsideVolume(index1, index2, dimensions) {
        return (csUtils.indexWithinDimensions(index1, dimensions) &&
            csUtils.indexWithinDimensions(index2, dimensions));
    }
}
function getTextCallback(doneChangingTextCallback) {
    return doneChangingTextCallback(prompt('Enter your annotation:'));
}
function changeTextCallback(data, eventData, doneChangingTextCallback) {
    return doneChangingTextCallback(prompt('Enter your annotation:'));
}
LabelTool.toolName = 'Label';
export default LabelTool;
