extern crate num;
extern crate image;

use std::str::FromStr;
use num::Complex;
use image::ColorType;
use image::png::PNGEncoder;
use std::fs::File;
use std::io::Result;

fn escapes(c: Complex<f64>, limit: u32) -> Option<u32> {
    let mut z = Complex {re: 0.0, im: 0.0};
    for i in 0..limit {
        z = z*z + c;
        if z.norm_sqr() > 4.0 {
            return Some(i);
        }
    }

    return None;
}

pub fn 
(pixels: &mut [u8],
          bounds: (usize, usize),
          upper_left: (f64, f64),
          lower_right: (f64, f64)) {
    assert!(pixels.len() == bounds.0 * bounds.1);

    for row in 0 .. bounds.1 {
        for column in 0 .. bounds.0 {
            let point = pixel_to_point(bounds, (column,row),
                                       upper_left, lower_right);
            pixels[row * bounds.0 + column] = 
                match escapes(Complex {re: point.0, im: point.1}, 255) {
                    None => 0,
                    Some(count) => 255 - count as u8,
                };
        }
    }
}

pub fn write_bitmap(filename: &str, pixels: &[u8], bounds: (usize, usize))
    -> Result<()> {
    let output = try!(File::create(filename));

    let encoder = PNGEncoder::new(output);
    try!(encoder.encode(&pixels[..],
                        bounds.0 as u32, bounds.1 as u32,
                        ColorType::Gray(8)));

    Ok(())
}

pub fn parse_pair<T: FromStr>(s: &str, separator: char) -> Option<(T, T)> {
    match s.find(separator) {
        None => None,
        Some(index) => {
            match (T::from_str(&s[..index]), T::from_str(&s[index+1..])) {
                (Ok(l), Ok(r)) => Some((l, r)),
                _ => None
            }
        }
    }
}

fn pixel_to_point(bounds: (usize, usize),
                   pixel: (usize, usize),
                   upper_left: (f64, f64),
                   lower_right: (f64, f64))
    -> (f64, f64)
{
    let (width, height) = (lower_right.0 - upper_left.0,
                           upper_left.1 - lower_right.1);
    (upper_left.0 + pixel.0 as f64 * width / bounds.0 as f64,
     upper_left.1 - pixel.1 as f64 * height / bounds.1 as f64)
}

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

	#[test]
	fn test_parse_pair() {
		assert_eq!(parse_pair::<i32>("", ','), None);
		assert_eq!(parse_pair::<i32>("10,", ','), None);
		assert_eq!(parse_pair::<i32>(",10", ','), None);
		assert_eq!(parse_pair::<i32>("10,20", ','), Some((10, 20)));
	}

    #[test]
    fn test_pixel_to_point() {
        assert_eq!(pixel_to_point((100, 100), (25, 75),
                                  (-1.0, 1.0), (1.0, -1.0)),
                                  (-0.5, -0.5));
    }
}

render