pub struct LumaImageBuffer<'a> {
    pub width: usize,
    pub height: usize,
    pub buffer: &'a [u8],
}

const BLACK_THRESHOLD: u8 = 10;
const WHITE_THRESHOLD: u8 = 245;
const MAX_DEVIATION_RATIO: f32 = 0.02;

fn trim(img: &LumaImageBuffer<'_>) -> (usize, usize, usize, usize) {
    let mut min_x = img.width;
    let mut min_y = img.height;
    let mut max_x = 0;
    let mut max_y = 0;

    for y in 0..img.height {
        for x in 0..img.width {
            let v = img.buffer[y * img.width + x];
            if v > BLACK_THRESHOLD {
                min_x = min_x.min(x);
                min_y = min_y.min(y);
                max_x = max_x.max(x);
                max_y = max_y.max(y);
            }
        }
    }

    if min_x > max_x || min_y > max_y {
        return (0, 0, 0, 0);
    }

    (min_x, min_y, max_x + 1, max_y + 1)
}

fn band(v: u8) -> u8 {
    if v <= BLACK_THRESHOLD {
        0
    } else if v <= WHITE_THRESHOLD {
        1
    } else {
        2
    }
}

pub fn compare_image(img1: &LumaImageBuffer<'_>, img2: &LumaImageBuffer<'_>) -> (bool, f32) {
    compare_image_with_threshold(img1, img2, MAX_DEVIATION_RATIO)
}

pub fn compare_image_with_threshold(
    img1: &LumaImageBuffer<'_>,
    img2: &LumaImageBuffer<'_>,
    max_deviation_ratio: f32,
) -> (bool, f32) {
    let (img1_fromx, img1_fromy, img1_tox, img1_toy) = trim(img1);
    let (img2_fromx, img2_fromy, img2_tox, img2_toy) = trim(img2);

    let width1 = img1_tox.saturating_sub(img1_fromx);
    let height1 = img1_toy.saturating_sub(img1_fromy);
    let width2 = img2_tox.saturating_sub(img2_fromx);
    let height2 = img2_toy.saturating_sub(img2_fromy);

    let compare_width = width1.min(width2);
    let compare_height = height1.min(height2);

    if width1 == 0 || height1 == 0 || width2 == 0 || height2 == 0 {
        return (false, 0.);
    }

    // Try different offsets, sometimes we're 1 pixel above or below swash.
    let mut deviations = 0usize;
    let mut total = 0usize;

    for y in 0..compare_height {
        for x in 0..compare_width {
            let y1 = img1_fromy + y;
            let y2 = img2_fromy + y;

            let x1 = img1_fromx + x;
            let x2 = img2_fromx + x;

            let v1 = img1.buffer[y1 * img1.width + x1];
            let v2 = img2.buffer[y2 * img2.width + x2];

            let does_match = band(v1) == band(v2);
            if !does_match {
                deviations += 1;
            }

            total += 1;
        }
    }

    if total == 0 {
        total = 1;
    }

    let ratio = deviations as f32 / total as f32;
    (ratio <= max_deviation_ratio, ratio)
}
