//! The resampler a ladder rung's pictures are made with before they are encoded
//! ([platform-encode] §4.2): separable two-tap interpolation in integers, so every host
//! hands its encoder the same samples and a verifier holding the source recomputes them.
//!
//! For an axis of `n_in` samples resampled to `n_out`, output sample `o` reads the
//! source at `p = ((2·o + 1) · n_in · 128) / n_out − 128` in 1/256ths of a sample
//! (integer division), clamped to `0 ..= 256 · (n_in − 1)`; with `x0 = p >> 8`,
//! `f = p & 255` and `x1 = min(x0 + 1, n_in − 1)`, the sample is
//! `(s[x0] · (256 − f) + s[x1] · f + 128) >> 8`. Rows are resampled first, then
//! columns, each pass rounding to 8 bits. Each plane is resampled on its own, chroma
//! at half the luma size.
//!
//! [platform-encode]: ../../../../extensions/platform-encode.md

/// The resampler's name in a record's `settings.resample`.
pub const NAME: &str = "wai.resample.two-tap";

fn taps(n_in: usize, n_out: usize) -> Vec<(usize, usize, u32)> {
    (0..n_out)
        .map(|o| {
            let p = ((2 * o as i64 + 1) * n_in as i64 * 128) / n_out as i64 - 128;
            let p = p.clamp(0, 256 * (n_in as i64 - 1)) as usize;
            let x0 = p >> 8;
            (x0, (x0 + 1).min(n_in - 1), (p & 255) as u32)
        })
        .collect()
}

fn lerp(a: u8, b: u8, f: u32) -> u8 {
    ((u32::from(a) * (256 - f) + u32::from(b) * f + 128) >> 8) as u8
}

/// Resample one plane of `w × h` to `ow × oh`.
pub fn plane(src: &[u8], w: usize, h: usize, ow: usize, oh: usize) -> Vec<u8> {
    if (w, h) == (ow, oh) {
        return src.to_vec();
    }
    let tx = taps(w, ow);
    let mut mid = vec![0u8; ow * h];
    for y in 0..h {
        let row = &src[y * w..(y + 1) * w];
        for (o, &(a, b, f)) in tx.iter().enumerate() {
            mid[y * ow + o] = lerp(row[a], row[b], f);
        }
    }
    let ty = taps(h, oh);
    let mut out = vec![0u8; ow * oh];
    for (o, &(a, b, f)) in ty.iter().enumerate() {
        for x in 0..ow {
            out[o * ow + x] = lerp(mid[a * ow + x], mid[b * ow + x], f);
        }
    }
    out
}

/// Resample a 4:2:0 picture (`w`, `h` even) held as three planes one after another.
pub fn i420(src: &[u8], w: usize, h: usize, ow: usize, oh: usize) -> Vec<u8> {
    let (cw, ch, ocw, och) = (w / 2, h / 2, ow / 2, oh / 2);
    let mut out = plane(&src[..w * h], w, h, ow, oh);
    out.extend(plane(&src[w * h..w * h + cw * ch], cw, ch, ocw, och));
    out.extend(plane(&src[w * h + cw * ch..w * h + 2 * cw * ch], cw, ch, ocw, och));
    out
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn identity_and_halving() {
        let src: Vec<u8> = (0..16).map(|i| i * 16).collect();
        assert_eq!(plane(&src, 4, 4, 4, 4), src);
        // 4 → 2 along each axis reads each output halfway between two inputs.
        assert_eq!(plane(&[0, 100, 200, 250], 4, 1, 2, 1), vec![50, 225]);
        // A constant plane stays constant at any size.
        assert!(plane(&[77; 30 * 20], 30, 20, 17, 9).iter().all(|&v| v == 77));
    }
}
