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

/**
 *
 * @param {Sampler2D} input
 * @param {Sampler2D} output
 * @param {(number)=>number|{support:number}} weight_computer
 * @param {number} support size of support kernel
 */
export function sampler2d_scale_down_generic(
    input, output,
    weight_computer, support = weight_computer.support
) {

    const d_w = output.width;
    const d_h = output.height;

    const item_size = output.itemSize;

    const source_width = input.width;
    const source_height = input.height;

    if (source_width < d_w || source_height < d_h) {
        throw new Error('Invalid scale, source is larger than destination in at least 1 dimension');
    }

    const source_data = input.data;
    const destination_data = output.data;

    const ratio_x = source_width / d_w;
    const ratio_y = source_height / d_h;

    const rcp_ratio_y = 2 / ratio_y;
    const rcp_ratio_x = 2 / ratio_x;


    const range2_x = Math.ceil(ratio_x * support / 2);
    const range2_y = Math.ceil(ratio_y * support / 2);

    const sample = new Float64Array(item_size);

    let v, u, i, j, k;

    for (v = 0; v < d_h; v++) {

        const center_y = (v + 0.5) * ratio_y;
        const i_center_y = Math.floor(center_y);
        //
        const source_y0 = max2(0, i_center_y - range2_y);
        const source_y1 = min2(i_center_y + range2_y, source_height - 1);

        for (u = 0; u < d_w; u++) {
            const center_x = (u + 0.5) * ratio_x;
            const i_center_x = Math.floor(center_x);

            const source_x0 = max2(i_center_x - range2_x, 0);
            const source_x1 = min2(i_center_x + range2_x, source_width - 1);

            let z = 0;
            for (j = source_y0; j <= source_y1; j++) {

                const f_y = (j - center_y) + 0.5;

                const f_y_rcp = f_y * rcp_ratio_y;
                const f_y_rcp2 = f_y_rcp * f_y_rcp;

                for (i = source_x0; i <= source_x1; i++) {

                    const f_x = (i - center_x) + 0.5;

                    const f_x_rcp = f_x * rcp_ratio_x;
                    const f_x_rcp2 = f_x_rcp * f_x_rcp;

                    const distance_from_center_sqr = f_x_rcp2 + f_y_rcp2;
                    const distance_from_center = Math.sqrt(distance_from_center_sqr);

                    const weight = weight_computer(distance_from_center);

                    if (weight <= 0) {
                        continue;
                    }

                    z += weight;

                    const source_texel_address = (j * source_width + i) * item_size;

                    for (k = 0; k < item_size; k++) {
                        sample[k] += weight * source_data[source_texel_address + k];
                    }

                }
            }


            // dilute sample and write to destination
            const inv_z = 1 / z;

            const texel_address = (v * d_w + u) * item_size;

            for (i = 0; i < item_size; i++) {
                destination_data[texel_address + i] = sample[i] * inv_z;

                // reset sample for next tap
                sample[i] = 0;
            }
        }
    }

}
