#![allow(clippy::many_single_char_names)]
use rand;
use rand::Rng;

use crate::texture::Texture;
use crate::vec3::Vec3;

#[derive(Debug)]
pub struct Mandelbrot {
    scale: f32,
    palette: Vec<Vec3>,
}

// HSV values in [0..1]
// returns [r, g, b] values from 0 to 255
//From https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
fn hsv_to_rgb(h: f32, s: f32, v: f32) -> Vec3 {
    let h_i = (h * 6.) as i32;
    let f = h * 6. - h_i as f32;
    let p = v * (1. - s);
    let q = v * (1. - f * s);
    let t = v * (1. - (1. - f) * s);
    match h_i {
        0 => Vec3::new(v, t, p),
        1 => Vec3::new(q, v, p),
        2 => Vec3::new(p, v, t),
        3 => Vec3::new(p, q, v),
        4 => Vec3::new(t, p, v),
        5 => Vec3::new(v, p, q),
        _ => panic!("Unknown H value {}", h_i),
    }
}

fn generate_palette(num: usize) -> Vec<Vec3> {
    let mut rng = rand::thread_rng();
    let mut random = || rng.gen();
    // use golden ratio
    let golden_ratio_conjugate = 0.618_034;
    let mut h = random();
    (0..num)
        .map(|_| {
            h += golden_ratio_conjugate;
            h %= 1.0;
            hsv_to_rgb(h, 0.99, 0.99)
        })
        .collect()
}

impl Default for Mandelbrot {
    fn default() -> Self {
        Mandelbrot {
            scale: 2.0,
            palette: generate_palette(10),
        }
    }
}

impl Texture for Mandelbrot {
    fn value(&self, u: f32, v: f32, _p: Vec3) -> Vec3 {
        // scaled x coordinate of pixel (scaled to lie in the Mandelbrot X scale (-2.5, 1))
        let x0 = u * 3.5 - 2.5;
        // scaled y coordinate of pixel (scaled to lie in the Mandelbrot Y scale (-1, 1))
        let y0 = v * 2.0 - 1.0;
        let mut x = 0.0;
        let mut y = 0.0;
        let mut iteration = 0;
        let max_iteration = 1000;
        while (x * x + y * y) <= 2. * 2. && iteration < max_iteration {
            let xtemp = x * x - y * y + x0;
            y = 2. * x * y + y0;
            x = xtemp;
            iteration += 1;
        }
        if iteration == max_iteration {
            return Vec3::default();
        }
        self.palette[iteration % self.palette.len()]
    }
}
