import { utilities as csUtils, cache, volumeLoader } from '@cornerstonejs/core';
import { run } from './runGrowCut';
const { transformWorldToIndex, transformIndexToWorld } = csUtils;
const POSITIVE_SEED_VALUE = 254;
const NEGATIVE_SEED_VALUE = 255;
const POSITIVE_SEED_VARIANCE = 0.1;
const NEGATIVE_SEED_VARIANCE = 0.8;
function _generateSeeds(referencedVolume, labelmap, worldPosition, options) {
    const [width, height, numSlices] = referencedVolume.dimensions;
    const subVolPixelData = referencedVolume.voxelManager.getCompleteScalarDataArray();
    const numPixelsPerSlice = width * height;
    const ijkStartPosition = transformWorldToIndex(referencedVolume.imageData, worldPosition);
    const startIndex = ijkStartPosition[2] * numPixelsPerSlice +
        ijkStartPosition[1] * width +
        ijkStartPosition[0];
    const referencePixelValue = subVolPixelData[startIndex];
    const positiveSeedVariance = options.positiveSeedVariance ?? POSITIVE_SEED_VARIANCE;
    const positiveSeedVarianceValue = Math.abs(referencePixelValue * positiveSeedVariance);
    const minPositivePixelValue = referencePixelValue - positiveSeedVarianceValue;
    const maxPositivePixelValue = referencePixelValue + positiveSeedVarianceValue;
    const negativeSeedVariance = options.negativeSeedVariance ?? NEGATIVE_SEED_VARIANCE;
    const negativeSeedVarianceValue = Math.abs(referencePixelValue * negativeSeedVariance);
    const minNegativePixelValue = referencePixelValue - negativeSeedVarianceValue;
    const maxNegativePixelValue = referencePixelValue + negativeSeedVarianceValue;
    const positiveSeedValue = options.positiveSeedValue ?? POSITIVE_SEED_VALUE;
    const negativeSeedValue = options.negativeSeedValue ?? NEGATIVE_SEED_VALUE;
    const neighborsCoordDelta = [
        [-1, 0, 0],
        [1, 0, 0],
        [0, -1, 0],
        [0, 1, 0],
        [0, 0, -1],
        [0, 0, 1],
    ];
    let minX = Infinity, minY = Infinity, minZ = Infinity, maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
    const voxelIndexesSet = new Set([startIndex]);
    labelmap.voxelManager.setAtIndex(startIndex, positiveSeedValue);
    const queue = [ijkStartPosition];
    while (queue.length) {
        const [x, y, z] = queue.shift();
        minX = x < minX ? x : minX;
        minY = y < minY ? y : minY;
        minZ = z < minZ ? z : minZ;
        maxX = x > maxX ? x : maxX;
        maxY = y > maxY ? y : maxY;
        maxZ = z > maxZ ? z : maxZ;
        for (let i = 0; i < neighborsCoordDelta.length; i++) {
            const [dx, dy, dz] = neighborsCoordDelta[i];
            const nx = x + dx;
            const ny = y + dy;
            const nz = z + dz;
            if (nx < 0 ||
                nx >= width ||
                ny < 0 ||
                ny >= height ||
                nz < 0 ||
                nz >= numSlices) {
                continue;
            }
            const neighborVoxelIndex = nz * numPixelsPerSlice + ny * width + nx;
            if (voxelIndexesSet.has(neighborVoxelIndex)) {
                continue;
            }
            const neighborPixelValue = subVolPixelData[neighborVoxelIndex];
            if (neighborPixelValue >= minPositivePixelValue &&
                neighborPixelValue <= maxPositivePixelValue) {
                labelmap.voxelManager.setAtIndex(neighborVoxelIndex, positiveSeedValue);
                voxelIndexesSet.add(neighborVoxelIndex);
                queue.push([nx, ny, nz]);
            }
        }
    }
    const margin = options.negativeSeedMargin ?? 30;
    const minXwMargin = Math.max(0, minX - margin);
    const minYwMargin = Math.max(0, minY - margin);
    const minZwMargin = Math.max(0, minZ - margin);
    const maxXwMargin = Math.min(width - 1, maxX + margin);
    const maxYwMargin = Math.min(height - 1, maxY + margin);
    const maxZwMargin = Math.min(numSlices - 1, maxZ + margin);
    const negativeSeedsCount = options.negativeSeedsCount ?? 70;
    const negativeIndexesSet = new Set();
    let attempts = 0;
    while (negativeIndexesSet.size < negativeSeedsCount &&
        attempts < negativeSeedsCount * 50) {
        attempts++;
        const rx = Math.floor(Math.random() * (maxXwMargin - minXwMargin + 1) + minXwMargin);
        const ry = Math.floor(Math.random() * (maxYwMargin - minYwMargin + 1) + minYwMargin);
        const rz = Math.floor(Math.random() * (maxZwMargin - minZwMargin + 1) + minZwMargin);
        const randomIndex = rz * numPixelsPerSlice + ry * width + rx;
        if (voxelIndexesSet.has(randomIndex)) {
            continue;
        }
        const randomVal = subVolPixelData[randomIndex];
        if (randomVal >= minNegativePixelValue &&
            randomVal <= maxNegativePixelValue) {
            labelmap.voxelManager.setAtIndex(randomIndex, negativeSeedValue);
            negativeIndexesSet.add(randomIndex);
        }
    }
}
async function runOneClickGrowCut({ referencedVolumeId, worldPosition, options, }) {
    const referencedVolume = cache.getVolume(referencedVolumeId);
    const labelmap = volumeLoader.createAndCacheDerivedLabelmapVolume(referencedVolumeId);
    _generateSeeds(referencedVolume, labelmap, worldPosition, options);
    await run(referencedVolumeId, labelmap.volumeId);
    return labelmap;
}
export { runOneClickGrowCut as default, runOneClickGrowCut };
