use empfindung::cie00;
use lab::Lab;

use crate::indexed::redmean_sq;


fn ciede2000(a: [u8;3], b: [u8;3]) -> f32 {
    cie00::diff(Lab::from_rgb(&a), Lab::from_rgb(&b)) // / 100.0
}

fn ciede2000_sq(a: [u8;3], b: [u8;3]) -> f32 {
    let diff = ciede2000(a, b);
    diff * diff
}

fn redmean(a: [u8;3], b: [u8;3]) -> f32 {
    redmean_sq(a, b).sqrt()
}

#[cfg(test)]
mod test {
    use core::f32;
    use std::f32::INFINITY;

    use rand::prelude::*;

    use super::ciede2000;

    fn cost(params: [f32; 3]) -> f32 {
        let decode = |x: u32| -> [u8; 3] {
            let r = (x >> 8) as u8;
            let g = ((x >> 4) & 15) as u8;
            let b = (x & 15) as u8;
            [r | r<<4, g | g<<4, b | b<<4]
        };
        let mut result = 0.0;
        // TODO: precompute
        for color1 in 0..4096 {
            if color1 % 73 != 0 {
                continue;
            }
            let rgb1 = decode(color1);
            for color2 in (color1 + 1)..4096 {
                if color2 % 73 != 0 {
                    continue;
                }
                let rgb2 = decode(color2);
                let true_diff = ciede2000(rgb1, rgb2);
                let mut approx_diff = 0.0;
                for i in 0..3 {
                    let delta = rgb1[i] as f32 - rgb2[i] as f32;
                    approx_diff += params[i] * delta * delta;
                }
                approx_diff = approx_diff.sqrt();
                let err = true_diff - approx_diff;
                result += err * err;
            }
        }
        result
    }


    #[test]
    fn tinker() {
        let mut params = [1.0; 3];
        let mut best_params = [1.0; 3];
        let mut best_cost = f32::INFINITY;
        let mut scale = 1.0;
        for i in 0..5000 {
            if i > 4990 {
                dbg!((i, best_params, best_cost));
            }
            for c in 0..3 {
                params[c] += scale * thread_rng().gen_range(-0.01..0.01f32);
            }
            let new_cost = cost(params);
            if new_cost < best_cost {
                best_cost = new_cost;
                best_params = params;
            } else {
                params = best_params;
                scale *= 0.99999;
            }
        }
        dbg!((best_params, best_cost));
    }
}
