use cubecl::{calculate_cube_count_elemwise, prelude::*};

use crate::{tensor::JitTensor, FloatElement, JitRuntime};

#[cube(launch_unchecked)]
fn interpolate_nearest_kernel<F: Float>(input: &Tensor<F>, output: &mut Tensor<F>) {
    if ABSOLUTE_POS >= output.len() {
        return;
    }

    let batch = ABSOLUTE_POS / output.stride(0) % output.shape(0);
    let channel = ABSOLUTE_POS / output.stride(1) % output.shape(1);
    let y = ABSOLUTE_POS / output.stride(2) % output.shape(2);
    let x = ABSOLUTE_POS / output.stride(3) % output.shape(3);

    let factor = F::cast_from(y);
    let numerator = F::cast_from(input.shape(2));
    let denominator = F::cast_from(output.shape(2));
    let y: F = Floor::floor(factor * numerator / denominator);

    let factor = F::cast_from(x);
    let numerator = F::cast_from(input.shape(3));
    let denominator = F::cast_from(output.shape(3));
    let x: F = Floor::floor(factor * numerator / denominator);

    let index = batch * input.stride(0)
        + channel * input.stride(1)
        + u32::cast_from(y) * input.stride(2)
        + u32::cast_from(x) * input.stride(3);

    output[ABSOLUTE_POS] = input[index];
}

pub(crate) fn interpolate_nearest_launch<R: JitRuntime, E: FloatElement>(
    input: JitTensor<R>,
    output: JitTensor<R>,
) -> JitTensor<R> {
    let cube_dim = CubeDim::default();
    let cube_count = calculate_cube_count_elemwise(output.shape.num_elements(), cube_dim);

    unsafe {
        interpolate_nearest_kernel::launch_unchecked::<E, R>(
            &input.client,
            cube_count,
            cube_dim,
            input.as_tensor_arg::<E>(1),
            output.as_tensor_arg::<E>(1),
        )
    };

    output
}
