//! Mutators that can be used to mutate an image.
//! These mutated images can be used as a "near copy"
use image::{Rgb, Rgba};
use imageproc::definitions::Image;
use imageproc::geometric_transformations::*;

pub trait Mutator {
	/// Name/description of the mutator
	fn info(&self) -> String;

	/// Short tag that represents the mutator.
	/// To be used in the filename such that it can be recognized as mutated image
	fn tag(&self) -> String {
		"MUT".to_string()
	}

	/// Returns the mutated form of the input image
	fn mutate(&self, img: &image::DynamicImage) -> image::DynamicImage;
}

/// Horizontal mirror
pub struct Flip;
impl Mutator for Flip {
	fn info(&self) -> String {
		"Horizontal flip".to_string()
	}
	fn tag(&self) -> String {
		".flip.".to_string()
	}
	fn mutate(&self, img: &image::DynamicImage) -> image::DynamicImage {
		img.fliph()
	}
}

/// Hue shift 
pub struct Hue;
impl Mutator for Hue {
	fn info(&self) -> String {
		"Hue shift".to_string()
	}
	fn tag(&self) -> String {
		".hue.".to_string()
	}
	fn mutate(&self, img: &image::DynamicImage) -> image::DynamicImage {
		img.huerotate(5)
	}
}

/// Unsharp mask
pub struct Sharp;
impl Mutator for Sharp {
	fn info(&self) -> String {
		"Unsharp mask".to_string()
	}
	fn tag(&self) -> String {
		".sharp.".to_string()
	}
	fn mutate(&self, img: &image::DynamicImage) -> image::DynamicImage {
		img.unsharpen(1.5, 20)
	}
}

pub struct Blur;
impl Mutator for Blur {
	fn info(&self) -> String {
		"Gaussian blur".to_string()
	}
	fn tag(&self) -> String {
		".blur.".to_string()
	}
	fn mutate(&self, img: &image::DynamicImage) -> image::DynamicImage {
		img.blur(1.5)
	}
}

pub struct BlurSharp;
impl Mutator for BlurSharp {
	fn info(&self) -> String {
		"Blur and sharpen".to_string()
	}
	fn tag(&self) -> String {
		".blsh.".to_string()
	}
	fn mutate(&self, img: &image::DynamicImage) -> image::DynamicImage {
		img.blur(1.5).unsharpen(1.5, 20)
	}
}

pub struct RotateCrop;
impl Mutator for RotateCrop {
	fn info(&self) -> String {
		"Rotate and crop".to_string()
	}
	fn tag(&self) -> String {
		".rcrop.".to_string()
	}
	fn mutate(&self, img: &image::DynamicImage) -> image::DynamicImage {
		let img: Image<Rgb<u8>> = img.to_rgb8();
		let default = *img.get_pixel(0, 0);
		image::DynamicImage::ImageRgb8(rotate_about_center(&img, 0.1, Interpolation::Bicubic, default))
	}
}

pub fn get_all_mutators() -> Vec<Box<dyn Mutator>> {
	let mutators: Vec<Box<dyn Mutator>> = //Vec::with_capacity(4);
	vec![
		Box::new(Flip),
		Box::new(Hue),
		Box::new(Sharp),
		Box::new(Blur),
	];
	mutators
}