import { Sampler2D } from "../Sampler2D.js";
import { sampler2d_scale_down_linear } from "./sampler2d_scale_down_linear.js";
import { inverseLerp } from "../../../../../core/math/inverseLerp.js";
import { lerp } from "../../../../../core/math/lerp.js";

/**
 * Emulated how downsampling is done in OpenGL using autogenerated mipmaps with LINEAR_MIPMAP_LINEAR parameters
 * @param {Sampler2D} source
 * @param {Sampler2D} destination
 */
export function sampler2d_scale_down_mipmap(source, destination) {
    let current_mip = source;
    let previous_mip = source;

    const itemSize = current_mip.itemSize;

    const dest_width = destination.width;
    const dest_height = destination.height;

    while (current_mip.width > dest_width && current_mip.width > 1) {
        const new_w = Math.floor(current_mip.width * 0.5);
        const new_h = Math.floor(current_mip.height * 0.5);


        const out = new Sampler2D(new current_mip.data.constructor(new_w * new_h * itemSize), itemSize, new_w, new_h);

        sampler2d_scale_down_linear(current_mip, out);

        previous_mip = current_mip;
        current_mip = out;
    }

    const mip_mix = inverseLerp(previous_mip.width, current_mip.width, dest_width);

    const m1_hp_x = 0.5 / previous_mip.width;
    const m1_hp_y = 0.5 / previous_mip.height;

    const m0_hp_x = 0.5 / current_mip.width;
    const m0_hp_y = 0.5 / current_mip.height;

    const m1_sx = (previous_mip.width + 1) / (previous_mip.width);
    const m1_sy = (previous_mip.height + 1) / (previous_mip.height);

    const m0_sx = (current_mip.width + 1) / (current_mip.width);
    const m0_sy = (current_mip.height + 1) / (current_mip.height);

    const destination_v_scale = dest_height > 1 ? 1 / (dest_height - 1) : 0;
    const destination_u_scale = dest_width > 1 ? 1 / (dest_width - 1) : 0;

    for (let y = 0; y < dest_height; y++) {
        const v = y * destination_v_scale;

        for (let x = 0; x < dest_width; x++) {

            const u = x * destination_u_scale;

            for (let i = 0; i < itemSize; i++) {

                const c0 = previous_mip.sampleChannelBilinearUV(u * m1_sx - m1_hp_x, v * m1_sy - m1_hp_y, i);
                const c1 = current_mip.sampleChannelBilinearUV(u * m0_sx - m0_hp_x, v * m0_sy - m0_hp_y, i);

                const c_out = lerp(c0, c1, mip_mix);

                const destination_address = (y * dest_width + x) * itemSize + i;

                destination.data[destination_address] = c_out;
            }
        }
    }

}
