import { assert } from "../../../../../core/assert.js";
import { max2 } from "../../../../../core/math/max2.js";
import { min2 } from "../../../../../core/math/min2.js";

/**
 * algorithm proposed by Borgefors, Chamfer distance [J. ACM 15 (1968) 600, Comput. Vis. Graph. Image Process. 34 (1986) 344], h
 * @param {Sampler2D} source
 * @param {Sampler2D} distanceField
 * @param {number} emptyValue
 * @param {number} d1 distance between two adjacent pixels in either x or y direction
 * @param {number} d2 distance between two diagonally adjacent pixels
 * @param {number} maxD highest value that distance field can hold
 */
export function computeUnsignedDistanceField_Chamfer(
    source,
    distanceField,
    emptyValue,
    d1,
    d2,
    maxD
) {

    assert.defined(source, 'source');
    assert.defined(distanceField, 'distanceField');
    assert.isNumber(emptyValue, 'emptyValue');
    assert.isNumber(d1, 'd1');
    assert.isNumber(d2, 'd2');
    assert.isNumber(maxD, 'maxD');

    const sourceData = source.data;
    const distanceFieldData = distanceField.data;

    const width = source.width;
    const height = source.height;

    const maxX = width - 1;
    const maxY = height - 1;

    let x, y, i, v;

    //initialize distance field
    const dataSize = height * width;
    for (i = 0; i < dataSize; i++) {
        if (sourceData[i] !== emptyValue) {
            distanceFieldData[i] = 0;
        } else {
            distanceFieldData[i] = maxD;
        }
    }

    //first pass (forward)
    for (y = 0; y < height; y++) {

        const y_m1 = max2(y - 1, 0);

        const y_w = y * width;

        const y_m1_w = y_m1 * width;

        for (x = 0; x < width; x++) {

            i = y_w + x;

            v = distanceFieldData[i];

            const x_m1 = max2(x - 1, 0);
            const x_p1 = min2(x + 1, maxX);

            const v0_i = x_m1 + y_m1_w;
            const v0 = distanceFieldData[v0_i] + d2;

            if (v0 < v) {
                distanceFieldData[i] = v0;
                v = v0;
            }

            const v1_i = x + y_m1_w;
            const v1 = distanceFieldData[v1_i] + d1;

            if (v1 < v) {
                distanceFieldData[i] = v1;
                v = v1;
            }

            const v2_i = x_p1 + y_m1_w;
            const v2 = distanceFieldData[v2_i] + d2;

            if (v2 < v) {
                distanceFieldData[i] = v2;
                v = v2;
            }

            const v3_i = x_m1 + y_w;
            const v3 = distanceFieldData[v3_i] + d1;

            if (v3 < v) {
                distanceFieldData[i] = v3;
            }
        }
    }

    //second pass (backward)
    for (y = maxY; y >= 0; y--) {
        const y_w = y * width;

        const y_p1 = min2(y + 1, maxY);
        const y_p1_w = y_p1 * width;

        for (x = maxX; x >= 0; x--) {
            const x_p1 = min2(x + 1, maxX);
            const x_m1 = max2(x - 1, 0);

            i = y_w + x;

            v = distanceFieldData[i];

            const v0_i = x_p1 + y_w;
            const v0 = distanceFieldData[v0_i] + d1;

            if (v0 < v) {
                distanceFieldData[i] = v0;
                v = v0;
            }

            const v1_i = x_m1 + y_p1_w;
            const v1 = distanceFieldData[v1_i] + d2;

            if (v1 < v) {
                distanceFieldData[i] = v1;
                v = v1;
            }

            const v2_i = x + y_p1_w;
            const v2 = distanceFieldData[v2_i] + d1;

            if (v2 < v) {
                distanceFieldData[i] = v2;
                v = v2;
            }

            const v3_i = x_p1 + y_p1_w;
            const v3 = distanceFieldData[v3_i] + d2;

            if (v3 < v) {
                distanceFieldData[i] = v3;
            }
        }
    }
}
