import { getEnabledElement, utilities as csUtils } from '@cornerstonejs/core';

const {
  utilities,
  annotation,
  cursors
} = cornerstoneTools
const { addAnnotation } = annotation.state
const { triggerAnnotationCompleted } = annotation.state
const { hideElementCursor } = cursors.elementCursor
const { getViewportIdsWithToolToRender } = utilities.viewportFilters
const { BasicStatsCalculator } = utilities.math.BasicStatsCalculator
const { triggerAnnotationRenderForViewportIds } = utilities

/**
 * FixedRadiusCircleTool lets you draw annotations that measures the statistics
 * such as area, max, mean and stdDev of a circular region of interest with a fixed radius.
 */
class FixedRadiusCircleROITool extends cornerstoneTools.CircleROITool {
  static toolName = 'FixedRadiusCircleROI';

  constructor(
    toolProps = {},
    defaultToolProps = {
      supportedInteractionTypes: ['Mouse', 'Touch'],
      configuration: {
        shadow: true,
        preventHandleOutsideImage: false,
        storePointData: false,
        centerPointRadius: 0,
        calculateStats: true,
        radius: 6, // Default radius in mm
        radiusUnit: 'mm',
        statsCalculator: BasicStatsCalculator,
        getTextLines: defaultGetTextLines,
        simplified: true,
      },
    }
  ) {
    super(toolProps, defaultToolProps);
  }

  /**
   * Based on the current position of the mouse and the current imageId to create
   * a CircleROI Annotation and stores it in the annotationManager
   *
   * @param evt -  EventTypes.NormalizedMouseEventType
   * @returns The annotation object.
   *
   */
  addNewAnnotation = (
    evt
  ) => {
    const eventDetail = evt.detail;
    const { currentPoints, element } = eventDetail;
    const worldPos = currentPoints.world;

    const enabledElement = getEnabledElement(element);
    const { viewport } = enabledElement;

    const camera = viewport.getCamera();
    const { viewPlaneNormal, viewUp } = camera;

    const referencedImageId = this.getReferencedImageId(
      viewport,
      worldPos,
      viewPlaneNormal,
      viewUp
    );

    const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
    const targetId = this.getTargetId(viewport);

    // Calculate end point based on fixed radius
    const radius = Number.isFinite(this.configuration.radius)
      ? this.configuration.radius
      : 10;
    // viewUp is a normalized vector.
    // We want a point 'radius' distance away from center.
    // We can use viewUp or any vector in the plane.
    const endPos = [
      worldPos[0] + viewUp[0] * radius,
      worldPos[1] + viewUp[1] * radius,
      worldPos[2] + viewUp[2] * radius,
    ];

    const annotation = {
      highlighted: true,
      invalidated: true,
      metadata: {
        toolName: this.getToolName(),
        viewPlaneNormal: [...viewPlaneNormal],
        viewUp: [...viewUp],
        FrameOfReferenceUID,
        referencedImageId,
        ...viewport.getViewReference({ points: [worldPos] }),
      },
      data: {
        label: '',
        handles: {
          textBox: {
            hasMoved: false,
            worldPosition: [0, 0, 0],
            worldBoundingBox: {
              topLeft: [0, 0, 0],
              topRight: [0, 0, 0],
              bottomLeft: [0, 0, 0],
              bottomRight: [0, 0, 0],
            },
          },
          points: [[...worldPos], [...endPos]],
          activeHandleIndex: null,
        },
        cachedStats: {
          [targetId]: {
            Modality: null,
            radius: null,
            radiusUnit: null,
            area: null,
            mean: null,
            stdDev: null,
            max: null,
            isEmptyArea: null,
            areaUnit: null,
            modalityUnit: null,
          },
        },
      },
    };

    addAnnotation(annotation, element);

    const viewportIdsToRender = getViewportIdsWithToolToRender(
      element,
      this.getToolName()
    );

    triggerAnnotationRenderForViewportIds(viewportIdsToRender);
    setTimeout(() => {
      triggerAnnotationCompleted(annotation);
    }, 0);

    return annotation;
  };

  /**
   * Overriding handleSelectedCallback to prevent resizing if needed,
   * or just to handle the "fixed" nature.
   * If we want it to be truly fixed radius, we should prevent dragging the second handle.
   */
  handleSelectedCallback = (
    evt,
    annotation,
    handle
  ) => {
    const eventDetail = evt.detail;
    const { element } = eventDetail;
    const { data } = annotation;

    const { points } = data.handles;
    let handleIndex = points.findIndex((p) => p === handle);

    // If handleIndex is 1 (the rim/radius handle), ignore the drag to keep radius fixed.
    if (handleIndex === 1) {
      return;
    }

    // Otherwise, allow moving the circle (center handle)
    // Implementation copied from CircleROITool because super.handleSelectedCallback is not available
    // as it is an arrow function on the parent instance.

    annotation.highlighted = true;

    let movingTextBox = false;

    if ((handle).worldPosition) {
      movingTextBox = true;
      handleIndex = undefined;
    }

    // Find viewports to render on drag.
    const viewportIdsToRender = getViewportIdsWithToolToRender(
      element,
      this.getToolName()
    );

    this.editData = {
      annotation,
      viewportIdsToRender,
      handleIndex,
      movingTextBox,
    };
    this._activateModify(element);

    hideElementCursor(element);

    triggerAnnotationRenderForViewportIds(viewportIdsToRender);

    evt.preventDefault();
  };
}

function defaultGetTextLines(data, targetId) {
  const cachedVolumeStats = data?.cachedStats?.[targetId];
  if (!cachedVolumeStats) {
    return [];
  }
  const {
    radius,
    radiusUnit,
    area,
    mean,
    stdDev,
    max,
    isEmptyArea,
    areaUnit,
    modalityUnit,
  } = cachedVolumeStats;
  const textLines = [];
  if (csUtils.isNumber(radius)) {
    const radiusLine = isEmptyArea
      ? `Radius: Oblique not supported`
      : `Radius: ${csUtils.roundNumber(radius)} ${radiusUnit}`;
    textLines.push(radiusLine);
  }

  if (csUtils.isNumber(area)) {
    const areaLine = isEmptyArea
      ? `Area: Oblique not supported`
      : `Area: ${csUtils.roundNumber(area)} ${areaUnit}`;
    textLines.push(areaLine);
  }

  if (csUtils.isNumber(mean)) {
    textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${modalityUnit}`);
  }

  if (csUtils.isNumber(max)) {
    textLines.push(`Max: ${csUtils.roundNumber(max)} ${modalityUnit}`);
  }

  if (csUtils.isNumber(stdDev)) {
    textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${modalityUnit}`);
  }

  return textLines;
}

export default FixedRadiusCircleROITool;
