import { assert } from "../../../../../core/assert.js";
import { BitSet } from "../../../../../core/binary/BitSet.js";

/**
 * Naive flood implementation of a distance field computation algorithm
 * @param {Sampler2D} source
 * @param {Sampler2D} destination
 * @param {number} emptyValue
 *
 * @author Alex Goldring
 * @copyright Company Named Limited (c) 2026
 */
export function computeSignedDistanceField_NaiveFlood(
    source,
    destination,
    emptyValue
) {
    assert.equal(source.itemSize, 1, `unsupported source.itemSize, expected 1, got '${source.itemSize}'`);
    assert.isNumber(emptyValue, 'emptyValue');

    let i, j;

    const distanceData = destination.data;

    const visited = new BitSet();

    const openSet = new BitSet();

    //use "flood" algorithm

    //mark all visible tiles as visited
    const samplerData = source.data;
    const totalCellCount = samplerData.length;

    function traverseNeighbours(index, visitor) {
        let i = index - (width + 1);

        const top = index > width;
        const bottom = index < (totalCellCount - width);

        const x = index % width;

        const left = x > 0;
        const right = x < width - 1;

        if (top) {
            if (left) {
                visitor(i);
            }

            visitor(i + 1);

            if (right) {
                visitor(i + 2);
            }
        }

        i += width;

        if (left) {
            visitor(i);
        }

        if (right) {
            visitor(i + 2);
        }

        i += width;

        if (bottom) {
            if (left) {
                visitor(i);
            }

            visitor(i + 1);

            if (right) {
                visitor(i + 2);
            }
        }

    }

    const width = destination.width;

    for (i = 0; i < totalCellCount; i++) {
        const sampleValue = samplerData[i];
        if (sampleValue !== emptyValue) {
            visited.set(i, true);
            //write distance data
            distanceData[i] = 0;
        } else {
            distanceData[i] = 255;
        }
    }

    //populate initial open set
    for (i = visited.nextSetBit(0); i !== -1; i = visited.nextSetBit(i + 1)) {
        j = 0;
        traverseNeighbours(i, function (neighbourIndex) {
            if (!visited.get(neighbourIndex)) {
                //increment number of not visited
                j++;
            }
        });

        if (j === 0) {
            //all neighbours are visited, we can safely ignore this cell
        } else {
            openSet.set(i, true);
        }
    }

    for (i = openSet.nextSetBit(0); i !== -1; i = openSet.nextSetBit(0)) {
        //remove from open set
        openSet.set(i, false);

        const value = distanceData[i];

        traverseNeighbours(i, function (neighbourIndex) {
            const neighbourValue = value + 1;

            if (visited.get(neighbourIndex)) {
                if (distanceData[neighbourIndex] <= neighbourValue) {
                    return;
                }
            } else {
                visited.set(neighbourIndex, true);
            }

            distanceData[neighbourIndex] = neighbourValue;
            //add neighbour to open set
            openSet.set(neighbourIndex, true);
        });
    }
}
