//! Descriptors that can be used to describe an image.
//! If two images are (almost) the same, their descriptions will be the same.

use image::GenericImageView;

pub trait Descriptor {
    /// Print the name of the descriptor, 
    fn info(&self) -> String;
    fn resize(&self, img: &image::DynamicImage) -> image::DynamicImage {
        img.grayscale().thumbnail_exact(8, 8)
    }
    fn describe(&self, img: &image::DynamicImage) -> u64;
    fn distance(&self, a: u64, b: u64) -> u64 {
        (a^b).count_ones().into()
    }
}

/// Interprets a 64-element array as an 8x8 matrix
/// returns a nicely printable string
fn print_matrix<T: ToString + std::fmt::Display>(array: [T; 64]) -> String {
    let mut output = String::new();
    for x in 0..8 {
        for y in 0..8 {
            let element = format!("{:.2}", &array[x*8+y]);
            let element = format!("{:8}\t", element);
            output.push_str(&element);
        }
        output.push('\n');
    }
    output
}

/// A struct representing a Discrete Cosine Transform descriptor
/// Transforms an image into an 8x8 grayscale version, and transforms it
/// into the frequency domain
pub struct DCT {
    quantization_matrix: [u8; 64]
}
pub mod dct;

fn median64<T: Ord + Copy>(values: &[T]) -> T {
    let mut sorted_values = values.to_vec();
    sorted_values.sort();
    let len = sorted_values.len();
    sorted_values[len/2]
}

pub struct Median;

impl Descriptor for Median {
    fn info(&self) -> String {
        "median".to_string()
    }

    fn describe(&self, img: &image::DynamicImage) -> u64 {
        let img = self.resize(img);
        let mut values: [u8; 64] = [0; 64];
        for (i, (_, _, pix)) in img.pixels().enumerate() {
            values[i] = pix[0];
        }
        let median = median64(&values);
        let mut mask: u64 = 0;
        img.save("debug.png").unwrap();
        for (_, _, pix) in img.pixels() {
            if pix[0] > median {
                mask += 1;
            }
            mask <<= 1;
        }
        mask
    }
}

pub fn get_all_descriptors() -> Vec<Box<dyn Descriptor>> {
	let descriptors: Vec<Box<dyn Descriptor>> =
        vec![Box::new(DCT::new()), Box::new(Median)];
	// descriptors.push(Box::new(DCT::new()));
	// descriptors.push(Box::new(Median));
	descriptors
}