import {
  eventTarget,
  triggerEvent,
  VolumeViewport,
  utilities as csUtils
} from '@cornerstonejs/core'

const { transformWorldToIndex } = csUtils
import * as cornerstoneTools from '@cornerstonejs/tools'
const {
  Enums,
  utilities,
  annotation,
  drawing

} = cornerstoneTools

// const { getWorldWidthAndHeightFromTwoPoints } = utilities.planar
// const { roundNumber } = utilities

const { getAnnotations } = annotation.state
const { isAnnotationVisible } = annotation.visibility
const { isAnnotationLocked } = annotation.locking
const drawHandlesSvg = drawing.drawHandles
const drawCircleSvg = drawing.drawCircle
const drawLinkedTextBoxSvg = drawing.drawLinkedTextBox
const { getTextBoxCoordsCanvas } = utilities.drawing
console.log(utilities)
// const getCanvasCircleRadius
// const getCanvasCircleCorners
// const { getCanvasCircleRadius, getCanvasCircleCorners } = utilities.math.circle
import {
  getCalibratedLengthUnits,
  getCalibratedAreaUnits,
  getCalibratedScale,
  getCalibratedAspect
} from './../js/getCalibratedUnits'
import { getModalityUnit } from './../js/getModalityUnit'
import { pointInShapeCallback } from './../js/pointInShapeCallback'
import { vec3 } from 'gl-matrix'
class CircleROITool extends cornerstoneTools.CircleROITool {
  static toolName;
  // touchDragCallback: any;
  // mouseDragCallback: any;
  // _throttledCalculateCachedStats: any;
  // editData: {
  //   annotation: any;
  //   viewportIdsToRender: Array<string>;
  //   handleIndex?: number;
  //   movingTextBox?: boolean;
  //   newAnnotation?: boolean;
  //   hasMoved?: boolean;
  // } | null;
  // isDrawing: boolean;
  // isHandleOutsideImage = false;

  constructor(
    toolProps,
    defaultToolProps
  ) {
    super(toolProps, defaultToolProps)

    this._throttledCalculateCachedStats = utilities.throttle(
      this._calculateCachedStats,
      100,
      { trailing: true }
    )

    // this._throttledCalculateCachedStats = utilities.throttle(
    //   this._calculateCachedStats,
    //   100,
    //   { trailing: true }
    // )
    // this._getTextLines = this.getTextLines
  }
  renderAnnotation = (enabledElement, svgDrawingHelper) => {
    let renderStatus = false
    const { viewport } = enabledElement
    const { element } = viewport
    // console.log(element)
    let annotations = getAnnotations(this.getToolName(), element)

    if (!annotations || (annotations && annotations.length === 0)) {
      return renderStatus
    }

    annotations = this.filterInteractableAnnotationsForElement(
      element,
      annotations
    )

    if (!annotations || (annotations && annotations.length === 0)) {
      return renderStatus
    }

    const targetId = this.getTargetId(viewport)

    const renderingEngine = viewport.getRenderingEngine()

    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 { handles } = data
      const { points, activeHandleIndex } = handles

      styleSpecifier.annotationUID = annotationUID

      const lineWidth = this.getStyle('lineWidth', styleSpecifier, annotation)
      const lineDash = this.getStyle('lineDash', styleSpecifier, annotation)
      const color = this.getStyle('color', styleSpecifier, annotation)

      const canvasCoordinates = points.map((p) =>
        viewport.worldToCanvas(p)
      )
      const center = canvasCoordinates[0]
      const radius = getCanvasCircleRadius(canvasCoordinates)
      const canvasCorners = getCanvasCircleCorners(canvasCoordinates)

      const { centerPointRadius } = this.configuration

      const modalityUnitOptions = {
        isPreScaled: utilities.viewport.isViewportPreScaled(viewport, targetId),

        isSuvScaled: this.isSuvScaled(
          viewport,
          targetId,
          annotation.metadata.referencedImageId
        )
      }

      // If cachedStats does not exist, or the unit is missing (as part of import/hydration etc.),
      // force to recalculate the stats from the points
      if (
        !data.cachedStats[targetId] ||
        data.cachedStats[targetId].areaUnit === undefined
      ) {
        data.cachedStats[targetId] = {
          Modality: null,
          area: null,
          max: null,
          mean: null,
          stdDev: null,
          areaUnit: null,
          radius: null,
          radiusUnit: null,
          perimeter: null
        }
        console.log(111111)
        this._calculateCachedStats(
          annotation,
          viewport,
          renderingEngine,
          enabledElement,
          modalityUnitOptions
        )
      } else if (annotation.invalidated) {
        console.log(22222)

        this._throttledCalculateCachedStats(
          annotation,
          viewport,
          renderingEngine,
          enabledElement,
          modalityUnitOptions
        )

        // If the invalidated data is as a result of volumeViewport manipulation
        // of the tools, we need to invalidate the related viewports data, so that
        // when scrolling to the related slice in which the tool were manipulated
        // we re-render the correct tool position. This is due to stackViewport
        // which doesn't have the full volume at each time, and we are only working
        // on one slice at a time.
        if (viewport instanceof VolumeViewport) {
          const { referencedImageId } = annotation.metadata

          // invalidate all the relevant stackViewports if they are not
          // at the referencedImageId
          for (const targetId in data.cachedStats) {
            if (targetId.startsWith('imageId')) {
              const viewports = renderingEngine.getStackViewports()

              const invalidatedStack = viewports.find((vp) => {
                // The stack viewport that contains the imageId but is not
                // showing it currently
                const referencedImageURI =
                  csUtils.imageIdToURI(referencedImageId)
                const hasImageURI = vp.hasImageURI(referencedImageURI)
                const currentImageURI = csUtils.imageIdToURI(
                  vp.getCurrentImageId()
                )
                return hasImageURI && currentImageURI !== referencedImageURI
              })

              if (invalidatedStack) {
                delete data.cachedStats[targetId]
              }
            }
          }
        }
      }

      // If rendering engine has been destroyed while rendering
      if (!viewport.getRenderingEngine()) {
        console.warn('Rendering Engine has been destroyed')
        return renderStatus
      }

      let activeHandleCanvasCoords

      if (!isAnnotationVisible(annotationUID)) {
        continue
      }

      if (
        !isAnnotationLocked(annotation) &&
        !this.editData &&
        activeHandleIndex !== null
      ) {
        // Not locked or creating and hovering over handle, so render handle.
        activeHandleCanvasCoords = [canvasCoordinates[activeHandleIndex]]
      }

      if (activeHandleCanvasCoords) {
        const handleGroupUID = '0'
        drawHandlesSvg(
          svgDrawingHelper,
          annotationUID,
          handleGroupUID,
          activeHandleCanvasCoords,
          {
            color
          }
        )
      }

      const dataId = `${annotationUID}-circle`
      const circleUID = '0'
      drawCircleSvg(
        svgDrawingHelper,
        annotationUID,
        circleUID,
        center,
        radius,
        {
          color,
          lineDash,
          lineWidth
        },
        dataId
      )

      // draw center point, if "centerPointRadius" configuration is valid.
      if (centerPointRadius > 0) {
        if (radius > 3 * centerPointRadius) {
          drawCircleSvg(
            svgDrawingHelper,
            annotationUID,
            `${circleUID}-center`,
            center,
            centerPointRadius,
            {
              color,
              lineDash,
              lineWidth
            }
          )
        }
      }

      renderStatus = true

      const options = this.getLinkedTextBoxStyle(styleSpecifier, annotation)
      if (!options.visibility) {
        data.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]
          }
        }
        continue
      }

      const textLines = this.configuration.getTextLines(data, targetId)
      if (!textLines || textLines.length === 0) {
        continue
      }

      // Poor man's cached?
      let canvasTextBoxCoords

      if (!data.handles.textBox.hasMoved) {
        canvasTextBoxCoords = getTextBoxCoordsCanvas(canvasCorners)

        data.handles.textBox.worldPosition =
          viewport.canvasToWorld(canvasTextBoxCoords)
      }

      const textBoxPosition = viewport.worldToCanvas(
        data.handles.textBox.worldPosition
      )

      const textBoxUID = '1'
      const boundingBox = drawLinkedTextBoxSvg(
        svgDrawingHelper,
        annotationUID,
        textBoxUID,
        textLines,
        textBoxPosition,
        canvasCoordinates,
        {},
        options
      )

      const { x: left, y: top, width, height } = boundingBox

      data.handles.textBox.worldBoundingBox = {
        topLeft: viewport.canvasToWorld([left, top]),
        topRight: viewport.canvasToWorld([left + width, top]),
        bottomLeft: viewport.canvasToWorld([left, top + height]),
        bottomRight: viewport.canvasToWorld([left + width, top + height])
      }
    }

    return renderStatus
  };
  _calculateCachedStats1 = (
    annotation,
    viewport,
    renderingEngine,
    enabledElement,
    modalityUnitOptions
  ) => {
    const data = annotation.data
    const { viewportId, renderingEngineId } = enabledElement

    const { points } = data.handles

    const canvasCoordinates = points.map((p) => viewport.worldToCanvas(p))
    const { viewPlaneNormal, viewUp } = viewport.getCamera()

    const [topLeftCanvas, bottomRightCanvas] = (
      getCanvasCircleCorners(canvasCoordinates)
    )

    const topLeftWorld = viewport.canvasToWorld(topLeftCanvas)
    const bottomRightWorld = viewport.canvasToWorld(bottomRightCanvas)
    const { cachedStats } = data

    const targetIds = Object.keys(cachedStats)
    const worldPos1 = topLeftWorld
    const worldPos2 = bottomRightWorld
    for (let i = 0; i < targetIds.length; i++) {
      const targetId = targetIds[i]

      const image = this.getTargetIdImage(targetId, renderingEngine)

      // If image does not exists for the targetId, skip. This can be due
      // to various reasons such as if the target was a volumeViewport, and
      // the volumeViewport has been decached in the meantime.
      if (!image) {
        continue
      }

      const { dimensions, imageData, metadata } = image

      const worldPos1Index = transformWorldToIndex(imageData, worldPos1)

      worldPos1Index[0] = Math.floor(worldPos1Index[0])
      worldPos1Index[1] = Math.floor(worldPos1Index[1])
      worldPos1Index[2] = Math.floor(worldPos1Index[2])

      const worldPos2Index = transformWorldToIndex(imageData, worldPos2)

      worldPos2Index[0] = Math.floor(worldPos2Index[0])
      worldPos2Index[1] = Math.floor(worldPos2Index[1])
      worldPos2Index[2] = Math.floor(worldPos2Index[2])

      // Check if one of the indexes are inside the volume, this then gives us
      // Some area to do stats over.

      if (this._isInsideVolume(worldPos1Index, worldPos2Index, dimensions)) {
        const iMin = Math.min(worldPos1Index[0], worldPos2Index[0])
        const iMax = Math.max(worldPos1Index[0], worldPos2Index[0])

        const jMin = Math.min(worldPos1Index[1], worldPos2Index[1])
        const jMax = Math.max(worldPos1Index[1], worldPos2Index[1])

        const kMin = Math.min(worldPos1Index[2], worldPos2Index[2])
        const kMax = Math.max(worldPos1Index[2], worldPos2Index[2])

        const boundsIJK = [
          [iMin, iMax],
          [jMin, jMax],
          [kMin, kMax]
        ]

        const center = [
          (topLeftWorld[0] + bottomRightWorld[0]) / 2,
          (topLeftWorld[1] + bottomRightWorld[1]) / 2,
          (topLeftWorld[2] + bottomRightWorld[2]) / 2
        ]

        const ellipseObj = {
          center,
          xRadius: Math.abs(topLeftWorld[0] - bottomRightWorld[0]) / 2,
          yRadius: Math.abs(topLeftWorld[1] - bottomRightWorld[1]) / 2,
          zRadius: Math.abs(topLeftWorld[2] - bottomRightWorld[2]) / 2
        }
        const { worldWidth, worldHeight } = getWorldWidthAndHeightFromTwoPoints(
          viewPlaneNormal,
          viewUp,
          worldPos1,
          worldPos2
        )
        const isEmptyArea = worldWidth === 0 && worldHeight === 0
        const scale = getCalibratedScale(image)
        const aspect = getCalibratedAspect(image)
        const area = Math.abs(
          Math.PI *
            (worldWidth / scale / 2) *
            (worldHeight / aspect / scale / 2)
        )

        const modalityUnit = getModalityUnit(
          metadata.Modality,
          annotation.metadata.referencedImageId,
          modalityUnitOptions
        )
        const pointsInShape = pointInShapeCallback(
          imageData,
          (pointLPS, pointIJK) => pointInEllipse(ellipseObj, pointLPS),
          this.configuration.statsCalculator.statsCallback,
          boundsIJK
        )

        const stats = this.configuration.statsCalculator.getStatistics()

        cachedStats[targetId] = {
          Modality: metadata.Modality,
          area,
          mean: stats[1] && stats[1].value ? stats[1].value : null,
          max: stats[0] && stats[0].value ? stats[0].value : null,
          stdDev: stats[2] && stats[2].value ? stats[2].value : null,
          statsArray: stats,
          pointsInShape: pointsInShape,
          isEmptyArea,
          areaUnit: getCalibratedAreaUnits(null, image),
          radius: worldWidth / 2 / scale,
          radiusUnit: getCalibratedLengthUnits(null, image),
          perimeter: (2 * Math.PI * (worldWidth / 2)) / scale,
          modalityUnit
        }
      } else {
        this.isHandleOutsideImage = true

        cachedStats[targetId] = {
          Modality: metadata.Modality
        }
      }
    }

    annotation.invalidated = false

    // Dispatching annotation modified
    const eventType = Enums.Events.ANNOTATION_MODIFIED

    const eventDetail = {
      annotation,
      viewportId,
      renderingEngineId
    }
    triggerEvent(eventTarget, eventType, eventDetail)

    return cachedStats
  }
}
function getCanvasCircleCorners(
  circleCanvasPoints
) {
  const [center, end] = circleCanvasPoints
  const radius = utilities.math.point.distanceToPoint(center, end)

  const topLeft = [center[0] - radius, center[1] - radius]
  const bottomRight = [center[0] + radius, center[1] + radius]

  return [topLeft, bottomRight]
}
function getCanvasCircleRadius(
  circleCanvasPoints
) {
  const [center, end] = circleCanvasPoints
  return utilities.math.point.distanceToPoint(center, end)
}
function getWorldWidthAndHeightFromTwoPoints(
  viewPlaneNormal,
  viewUp,
  worldPos1,
  worldPos2
) {
  const viewRight = vec3.create()

  vec3.cross(viewRight, viewUp, viewPlaneNormal)

  const pos1 = vec3.fromValues(...worldPos1)
  const pos2 = vec3.fromValues(...worldPos2)

  const diagonal = vec3.create()
  vec3.subtract(diagonal, pos1, pos2)

  const diagonalLength = vec3.length(diagonal)

  // When the two points are very close to each other return width as 0
  // to avoid NaN the cosTheta formula calculation
  if (diagonalLength < 0.0001) {
    return { worldWidth: 0, worldHeight: 0 }
  }

  const cosTheta =
    vec3.dot(diagonal, viewRight) / (diagonalLength * vec3.length(viewRight))

  const sinTheta = Math.sqrt(1 - cosTheta * cosTheta)

  const worldWidth = sinTheta * diagonalLength
  const worldHeight = cosTheta * diagonalLength

  return { worldWidth, worldHeight }
}
function pointInEllipse(
  ellipse,
  pointLPS
) {
  const { center: circleCenterWorld, xRadius, yRadius, zRadius } = ellipse
  const [x, y, z] = pointLPS
  const [x0, y0, z0] = circleCenterWorld

  let inside = 0
  if (xRadius !== 0) {
    inside += ((x - x0) * (x - x0)) / (xRadius * xRadius)
  }

  if (yRadius !== 0) {
    inside += ((y - y0) * (y - y0)) / (yRadius * yRadius)
  }

  if (zRadius !== 0) {
    inside += ((z - z0) * (z - z0)) / (zRadius * zRadius)
  }

  return inside <= 1
}
// function defaultGetTextLines(data, targetId) {
//   const cachedVolumeStats = data.cachedStats[targetId]
//   const {
//     radius,
//     radiusUnit,
//     area,
//     mean,
//     stdDev,
//     max,
//     isEmptyArea,
//     areaUnit,
//     modalityUnit
//   } = cachedVolumeStats

//   const textLines = []

//   if (radius) {
//     const radiusLine = isEmptyArea
//       ? `Radius: Oblique not supported`
//       : `Radius: ${roundNumber(radius)} ${radiusUnit}`
//     textLines.push(radiusLine)
//   }

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

//   if (mean) {
//     textLines.push(`Mean: ${roundNumber(mean)} ${modalityUnit}`)
//   }

//   if (max) {
//     textLines.push(`Max: ${roundNumber(max)} ${modalityUnit}`)
//   }

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

//   return textLines
// }

CircleROITool.toolName = 'CircleROI'
export default CircleROITool

