import { vec3 } from 'gl-matrix'

/**
 * For each point in the image (If boundsIJK is not provided, otherwise, for each
 * point in the provided bounding box), It runs the provided callback IF the point
 * passes the provided criteria to be inside the shape (which is defined by the
 * provided pointInShapeFn)
 *
 * @param imageData - The image data object.
 * @param dimensions - The dimensions of the image.
 * @param pointInShapeFn - A function that takes a point in LPS space and returns
 * true if the point is in the shape and false if it is not.
 * @param callback - A function that will be called for
 * every point in the shape.
 * @param boundsIJK - The bounds of the volume in IJK coordinates.
 */
function pointInShapeCallback(
  imageData,
  pointInShapeFn,
  callback,
  boundsIJK
) {
  let iMin, iMax, jMin, jMax, kMin, kMax

  let scalarData

  // if getScalarData is a method on imageData
  if ((imageData).getScalarData) {
    scalarData = (ImageData).getScalarData()
  } else {
    scalarData = (imageData)
      .getPointData()
      .getScalars()
      .getData()
  }

  const dimensions = imageData.getDimensions()

  if (!boundsIJK) {
    iMin = 0
    iMax = dimensions[0]
    jMin = 0
    jMax = dimensions[1]
    kMin = 0
    kMax = dimensions[2]
  } else {
    [[iMin, iMax], [jMin, jMax], [kMin, kMax]] = boundsIJK
  }

  const start = vec3.fromValues(iMin, jMin, kMin)

  const direction = imageData.getDirection()
  const rowCosines = direction.slice(0, 3)
  const columnCosines = direction.slice(3, 6)
  const scanAxisNormal = direction.slice(6, 9)

  const spacing = imageData.getSpacing()
  const [rowSpacing, columnSpacing, scanAxisSpacing] = spacing

  // @ts-ignore will be fixed in vtk-master
  const worldPosStart = imageData.indexToWorld(start)

  const rowStep = vec3.fromValues(
    rowCosines[0] * rowSpacing,
    rowCosines[1] * rowSpacing,
    rowCosines[2] * rowSpacing
  )

  const columnStep = vec3.fromValues(
    columnCosines[0] * columnSpacing,
    columnCosines[1] * columnSpacing,
    columnCosines[2] * columnSpacing
  )

  const scanAxisStep = vec3.fromValues(
    scanAxisNormal[0] * scanAxisSpacing,
    scanAxisNormal[1] * scanAxisSpacing,
    scanAxisNormal[2] * scanAxisSpacing
  )

  const yMultiple = dimensions[0]
  const zMultiple = dimensions[0] * dimensions[1]

  const pointsInShape = []
  for (let k = kMin; k <= kMax; k++) {
    for (let j = jMin; j <= jMax; j++) {
      for (let i = iMin; i <= iMax; i++) {
        const pointIJK = [i, j, k]
        const dI = i - iMin
        const dJ = j - jMin
        const dK = k - kMin

        const startWorld = worldPosStart

        const pointLPS = [
          startWorld[0] +
            dI * rowStep[0] +
            dJ * columnStep[0] +
            dK * scanAxisStep[0],
          startWorld[1] +
            dI * rowStep[1] +
            dJ * columnStep[1] +
            dK * scanAxisStep[1],
          startWorld[2] +
            dI * rowStep[2] +
            dJ * columnStep[2] +
            dK * scanAxisStep[2]
        ]

        if (pointInShapeFn(pointLPS, pointIJK)) {
          const index = k * zMultiple + j * yMultiple + i
          const value = scalarData[index]

          pointsInShape.push({ value, index, pointIJK, pointLPS })
          if (callback !== null) {
            callback({ value, index, pointIJK, pointLPS })
          }
        }
      }
    }
  }
  return pointsInShape
}
export { pointInShapeCallback }
