use image::{ImageBuffer, Rgb};
use rayon::prelude::*;

// Image size.
const IMGW: u32 = 1000;
const IMGH: u32 = 1000;

// Fractal settings.
const MAXITER: u32 = 600;
const ESCRAD: f64 = 2.4;

// Fractal bounds.
const XMIN: f64 = 2.0;
const XMAX: f64 = -2.0;
const YMIN: f64 = 2.0;
const YMAX: f64 = -2.0;

fn main() {
    let w = IMGW as f64;
    let h = IMGH as f64;

    let pxs: Vec<u8> = (0..IMGH)
        .into_par_iter()
        .flat_map(|y| {
            let fy = y as f64;
            (0..IMGW)
                .into_par_iter()
                .flat_map(move |x| {
                    let cx = map_range(x as f64, 0.0, w, XMIN, XMAX);
                    let cy = map_range(fy, 0.0, h, YMIN, YMAX);
                    let c = num_complex::Complex::new(cx, cy);

                    // Compute color for each pixel.
                    col_map(esc_time(c)).to_vec()
                })
                .collect::<Vec<u8>>()
        })
        .collect();

    // Create the image from the pixel buffer.
    let img = ImageBuffer::<Rgb<u8>, _>::from_raw(IMGW, IMGH, pxs).unwrap();

    img.save("h.png").unwrap();
    println!("Done.");
}

// Maps a value from one range to another.
fn map_range(val: f64, imin: f64, imax: f64, omin: f64, omax: f64) -> f64 {
    omin + (val - imin) * (omax - omin) / (imax - imin)
}

// Computes the escape time for a given point in the complex plane.
fn esc_time(c: num_complex::Complex<f64>) -> u32 {
    let mut z = c;
    for i in 0..MAXITER {
        if z.norm() > ESCRAD {
            return i;
        }
        z = (-z).ln() / z.ln();
    }
    MAXITER
}

// Maps the iteration count to a color.
fn col_map(iter: u32) -> [u8; 3] {
    if iter == MAXITER {
        [0, 0, 0] // Black for points that never escape.
    } else {
        let t = iter as f64 / MAXITER as f64;
        let c = (t * 700.0) as u8;
        [c, c, c]
    }
}
