//!
//! Fuss - Small, lightweight simplex noise generator for Rust
//! 
//! Ported from http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
//! originally in Java.
//! 

#![crate_type = "lib"]
#![crate_name = "fuss"]

mod test;

extern crate rand;
use rand::{thread_rng, SeedableRng, Rng, StdRng};

// Constants

// Skew 2D
const F2 : f32 = 0.366025403;
// Unskew 2D
const G2 : f32 = 0.211324865;

// Skew 3D
const F3 : f32 = 1.0/3.0;
// Unskew 3D
const G3 : f32 = 1.0/6.0;

const GRAD3: [(i8, i8, i8); 12] = [
  (1, 1, 0), (-1, 1, 0), (1, -1, 0), (-1, -1, 0),  
  (1, 0, 1), (-1, 0, 1), (1, 0, -1), (-1, 0, -1),  
  (0, 1, 1), (0, -1, 1), (0, 1, -1), (0, -1, -1),
];

// Vector math

///
/// Find dot product of a vector in 2 dimensions
///
#[inline]
fn dot2(g: (i8, i8, i8), x: f32, y: f32) -> f32 {
  g.0 as f32 * x + g.1 as f32 * y
}

///
/// Find dot product of a vector in 3 dimensions
/// 
#[inline]
fn dot3(g: (i8, i8, i8), x: f32, y: f32, z: f32) -> f32 {
  g.0 as f32 * x + g.1 as f32 * y + g.2 as f32 * z
}

// Generate a seed
#[inline]
pub fn generate_seed() -> Vec<usize> {
  thread_rng().gen_iter::<usize>().take(256).collect::<Vec<usize>>()
}

///
/// Hold the proper permutation tables and methods for generating 2D and 3D noise.
/// 
/// It is intended for you to get a `Simplex` through `Simplex::new()` since that
/// creates the necessary permutation tables needed to generate noise.
/// 
/// Noise generated by `Simplex` is random every time.
/// 
/// * `seed` - Seed that will be used by `Simplex` to generate it's permutation table
/// 
pub struct Simplex {

  pub seed: Vec<usize>,

  perm: Vec<u8>,

}

impl Simplex {

  ///
  /// Return a new `Simplex` with a new random permutation table
  /// 
  /// Necessary to generate the proper permutation tables (GRAD3)
  /// used by `noise_2d()` and `noise_3d`.
  /// 
  /// # Examples
  /// 
  /// ```
  /// use fuss::Simplex;
  /// 
  /// let sn = Simplex::new();
  /// ```
  /// 
  pub fn new() -> Simplex {

    return Simplex::from_seed(generate_seed());

  }

  ///
  /// Seed the random number generator with a specific 
  /// seed
  /// 
  /// A seed is just a vector of usizes that will be passed into
  /// `StdRng::from_seed` as a slice.
  /// 
  /// # Examples
  /// 
  /// ```
  /// use fuss::Simplex;
  /// 
  /// let mut sn = Simplex::from_seed(vec![1, 2, 3]);
  /// let mut other_sn = Simplex::from_seed(vec![1, 2, 3]);
  /// 
  /// assert_eq!(other_sn.noise_2d(1.0, 14.2), sn.noise_2d(1.0, 14.2));
  /// assert_eq!(other_sn.noise_3d(1.0, 14.2, -5.4), sn.noise_3d(1.0, 14.2, -5.4));
  /// 
  /// sn = Simplex::from_seed(vec![4, 5, 6]);
  /// let mut other_sn = Simplex::from_seed(vec![1, 2, 3]);
  /// assert!(other_sn.noise_2d(1.0, 14.2) != sn.noise_2d(1.0, 14.2));
  /// assert!(other_sn.noise_3d(1.0, 14.2, -5.4) != sn.noise_3d(1.0, 14.2, -5.4));
  /// ```
  /// 
  pub fn from_seed(seed: Vec<usize>) -> Simplex {
    let mut sn = Simplex { 
      seed: seed,
      perm: Vec::new() // Gets overwritten
    };
    sn.generate_perms();
    return sn;
  }

  ///
  /// Generate the permutation table
  /// 
  /// This method will overwrite the `Simplex`'s current `perm` vector
  /// with another random permutation table used by `noise_2d()` and
  /// `noise_3d`
  /// 
  fn generate_perms(&mut self) {
    let p : Vec<u8> = StdRng::from_seed(&self.seed).gen_iter().take(256).collect::<Vec<u8>>();
    self.perm = Vec::<u8>::new();
    for i in 0..512 {
      self.perm.push(p[(i & 255) as usize]);
    }
  }

  ///
  /// Smooth the output from `noise_2d` based on fractal Brownian motion.
  /// 
  /// Returns an f32 in [-1, 1]
  /// 
  /// # Examples
  /// 
  /// ```
  /// use fuss::Simplex;
  /// 
  /// let sn = Simplex::new();
  /// 
  /// let mut luminance = Vec::<Vec<f32>>::new();
  /// for x in 0..100 {
  ///   luminance.push(Vec::<f32>::new());
  ///   for y in 0..100 {
  ///     luminance[x as usize].push(sn.sum_octave_2d(16, x as f32, y as f32, 0.5, 0.008));
  ///   }
  /// }
  /// ```
  ///  
  pub fn sum_octave_2d(&self, num_iterations: isize, xin: f32, yin: f32, persistence : f32, scale : f32) -> f32 {

    let mut max_amp = 0.0;
    let mut amp = 1.0;
    let mut freq = scale;
    let mut noise = 0.0;

    // Add successively smaller, higher-frequency terms
    for _ in 0..num_iterations {
      noise += self.noise_2d(xin * freq, yin * freq) * amp;
      max_amp += amp;
      amp *= persistence;
      freq *= 2.0;
    }

    // Take the average value of the iterations
    return noise / max_amp;

  }

  ///
  /// Smooth the output from `noise_3d` based on fractal Brownian motion.
  /// 
  /// Returns an f32 in [-1, 1]
  /// 
  /// # Examples
  /// 
  /// ```
  /// use fuss::Simplex;
  /// 
  /// let sn = Simplex::new();
  /// 
  /// let mut luminance = Vec::<Vec<Vec<f32>>>::new();
  /// for x in 0..10 {
  ///   luminance.push(Vec::<Vec<f32>>::new());
  ///   for y in 0..10 {
  ///     luminance[x as usize].push(Vec::<f32>::new());
  ///     for z in 0..10 {
  ///       luminance[x as usize][y as usize].push(sn.sum_octave_3d(16, x as f32, y as f32, z as f32, 0.5, 0.008));
  ///     }
  ///   }
  /// }
  /// ```
  ///  
  pub fn sum_octave_3d(&self, num_iterations: isize, xin: f32, yin: f32, zin: f32, persistence : f32, scale : f32) -> f32 {

    let mut max_amp = 0.0;
    let mut amp = 1.0;
    let mut freq = scale;
    let mut noise = 0.0;

    // Add successively smaller, higher-frequency terms
    for _ in 0..num_iterations {
      noise += self.noise_3d(xin * freq, yin * freq, zin * freq) * amp;
      max_amp += amp;
      amp *= persistence;
      freq *= 2.0;
    }

    // Take the average value of the iterations
    return noise / max_amp;

  }
    
  ///
  /// Generate 2D simplex noise for a specific point
  /// 
  /// Returns an f32 in [-1, 1].
  /// 
  /// # Examples
  /// 
  /// ```
  /// use fuss::Simplex;
  /// 
  /// let sn = Simplex::from_seed(vec![5, 3, 2, 1, 1]);
  /// println!("{}", sn.noise_2d(50.1912, 30.50102));
  /// 
  /// // Simplex will return the same thing for the same points
  /// assert_eq!(sn.noise_2d(1.5, -0.5), sn.noise_2d(1.5, -0.5));
  /// 
  /// let other_sn = Simplex::from_seed(vec![0, 1, 2, 3, 4, 5]);
  /// 
  /// // However each `Simplex` has it's own set of permutations, therefore
  /// // each one is different. If you want consistency, try the `from_seed()` method.
  /// assert!(sn.noise_2d(1.5, -0.5) != other_sn.noise_2d(1.5, -0.5));
  /// ```
  ///
  pub fn noise_2d(&self, xin: f32, yin: f32) -> f32 {

    // Noise contributions from the three corners 
    let n0 : f32;
    let n1 : f32;
    let n2 : f32;

    // Hairy factor for 2D 
    let s = (xin + yin) * F2;
    let i = (xin + s).floor() as isize;
    let j = (yin + s).floor() as isize;

    let t = (i + j) as f32 * G2;

    // Unskew the cell origin back to (x,y) space 
    // and get he x,y distances from the cell origin 
    let x0 = xin - (i as f32 - t); 
    let y0 = yin - (j as f32 - t); 

    // For the 2D case, the simplex shape is an equilateral triangle. 
    // Determine which simplex we are in. 

    // Offsets for second (middle) corner of simplex in (i,j) coords 
    let i1 : isize;
    let j1 : isize; 

    // lower triangle, XY order: (0,0)->(1,0)->(1,1) 
    if x0 > y0 {
      i1 = 1;
      j1 = 0;
    }
    // upper triangle, YX order: (0,0)->(0,1)->(1,1) 
    else {
      i1 = 0; 
      j1 = 1;
    }

    // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and 
    // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where 
    // c = (3-sqrt(3))/6 

    // Offsets for middle corner in (x,y) unskewed coords 
    let x1 = x0 - i1 as f32 + G2; 
    let y1 = y0 - j1 as f32 + G2; 

    // Offsets for last corner in (x,y) unskewed coords 
    let x2 = x0 - 1.0 + 2.0 * G2; 
    let y2 = y0 - 1.0 + 2.0 * G2; 

    // Work out the hashed gradient indices of the three simplex corners 
    let ii = i & 255; 
    let jj = j & 255; 
    let gi0 = self.perm[(ii +    self.perm[jj as usize] as isize) as usize ] % 12; 
    let gi1 = self.perm[(ii + i1 + self.perm[(jj + j1) as usize] as isize) as usize ] % 12; 
    let gi2 = self.perm[(ii + 1 + self.perm[ (jj + 1) as usize] as isize) as usize] % 12; 
    
    let mut t0 = 0.5 - x0*x0-y0*y0; 
    if t0 < 0.0 {
      n0 = 0.0;
    } else { 
      t0 *= t0; 
      n0 = t0 * t0 * dot2(GRAD3[gi0 as usize], x0, y0);
    } 

    let mut t1 = 0.5 - x1*x1-y1*y1; 
    if t1 < 0. { 
      n1 = 0.0;
    } else { 
      t1 *= t1; 
      n1 = t1 * t1 * dot2(GRAD3[gi1 as usize], x1, y1); 
    }

    let mut t2 = 0.5 - x2*x2-y2*y2; 
    if t2 < 0.0 { 
      n2 = 0.0; 
    }
    else { 
      t2 *= t2; 
      n2 = t2 * t2 * dot2(GRAD3[gi2 as usize], x2, y2); 
    } 

    // Add contributions from each corner to get the final noise value. 
    // The result is scaled to return values in the interval [-1,1]. 
    return 70.0 * (n0 + n1 + n2);

  }

  ///
  /// Generate 3D simplex noise for a specific point
  /// 
  /// Returns an f32 in [-1, 1]. 
  /// 
  /// # Examples
  /// 
  /// ```
  /// use fuss::Simplex;
  /// 
  /// let sn = Simplex::new();
  /// println!("{}", sn.noise_2d(50.1912, 30.50102));
  /// 
  /// // Simplex will return the same thing for the same points
  /// assert_eq!(sn.noise_3d(1.5, -0.5, 2.1), sn.noise_3d(1.5, -0.5, 2.1));
  /// 
  /// let other_sn = Simplex::new();
  /// 
  /// // However each `Simplex` has it's own set of permutations, therefore
  /// // each one is different. If you want consistency, try the `from_seed()` method.
  /// assert!(sn.noise_3d(1.5, -0.5, 2.1) != other_sn.noise_3d(1.5, -0.5, 2.1));
  /// ```
  /// 
  pub fn noise_3d(&self, xin: f32, yin: f32, zin: f32) -> f32 {

    // Noise contributions from the four corners 
    let n0 : f32;
    let n1 : f32;
    let n2 : f32;
    let n3 : f32;

    // Very nice and simple skew factor for 3D 
    let s = (xin + yin + zin) * F3; 
    let i = (xin + s).floor(); 
    let j = (yin + s).floor(); 
    let k = (zin + s).floor(); 

    // Unskew the cell origin back to (x,y,z) space
    let t = (i + j + k) * G3; 

    // The x,y,z distances from the cell origin
    let x0 = xin - (i - t);  
    let y0 = yin - (j - t); 
    let z0 = zin - (k - t); 

    // For the 3D case, the simplex shape is a slightly 
    // irregular tetrahedron. 

    // Determine which simplex we are in. 

    // Offsets for second corner of simplex in (i,j,k) coords 
    let i1 : isize;
    let j1 : isize;
    let k1 : isize; 

    // Offsets for third corner of simplex in (i,j,k) coords 
    let i2 : isize;
    let j2 : isize;
    let k2 : isize;

    if x0 >= y0 { 
      if y0 >= z0 { 
        // X Y Z order 
        i1 = 1; 
        j1 = 0; 
        k1 = 0; 
        i2 = 1; 
        j2 = 1; 
        k2 = 0; 
      } else if x0 >= z0 { 
        // X Z Y order 
        i1=1; 
        j1=0; 
        k1=0; 
        i2=1; 
        j2=0; 
        k2=1; 
      } else { 
        // Z X Y order 
        i1=0; 
        j1=0; 
        k1=1; 
        i2=1; 
        j2=0; 
        k2=1; 
      } 
    } 
    // x0 < y0 
    else { 
      if y0 < z0 {
        // Z Y X order 
        i1=0; 
        j1=0; 
        k1=1; 
        i2=0; 
        j2=1; 
        k2=1; 
      } else if x0 < z0 { 
        // Y Z X order 
        i1=0; 
        j1=1; 
        k1=0; 
        i2=0; 
        j2=1; 
        k2=1; 
      } else { 
        // Y X Z order 
        i1=0; 
        j1=1; 
        k1=0; 
        i2=1; 
        j2=1; 
        k2=0;
      } 
    } 

    // A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z), 
    // a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and 
    // a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where 
    // c = 1/6.

    // Offsets for second corner in (x,y,z) coords
    let x1 = x0 - i1 as f32 + G3;  
    let y1 = y0 - j1 as f32 + G3; 
    let z1 = z0 - k1 as f32 + G3; 

    // Offsets for third corner in (x,y,z) coords 
    let x2 = x0 - i2 as f32 + 2.0*G3; 
    let y2 = y0 - j2 as f32 + 2.0*G3; 
    let z2 = z0 - k2 as f32 + 2.0*G3; 

    // Offsets for last corner in (x,y,z) coords 
    let x3 = x0 - 1.0 + 3.0*G3;
    let y3 = y0 - 1.0 + 3.0*G3; 
    let z3 = z0 - 1.0 + 3.0*G3; 

    // Work out the hashed gradient indices of the four simplex corners 
    let ii = i as isize & 255; 
    let jj = j as isize & 255; 
    let kk = k as isize & 255; 

    let gi0 = self.perm[(ii + self.perm[(jj + self.perm[kk as usize] as isize) as usize] as isize) as usize] % 12; 
    let gi1 = self.perm[(ii + i1 + self.perm[(jj + j1 + self.perm[(kk + k1) as usize] as isize) as usize] as isize) as usize] % 12; 
    let gi2 = self.perm[(ii + i2 + self.perm[(jj + j2 + self.perm[(kk + k2) as usize] as isize) as usize] as isize) as usize] % 12; 
    let gi3 = self.perm[(ii + 1 + self.perm [(jj + 1 + self.perm[kk as usize + 1] as isize) as usize] as isize) as usize] % 12; 
    
    // Calculate the contribution from the four corners 
    let mut t0 = 0.6 - x0*x0 - y0*y0 - z0*z0; 
    if t0 < 0.0 { 
      n0 = 0.0; 
    }
    else { 
      t0 *= t0; 
      n0 = t0 * t0 * dot3(GRAD3[gi0 as usize], x0, y0, z0); 
    }
    let mut t1 = 0.6 - x1*x1 - y1*y1 - z1*z1; 
    if t1 < 0.0 { 
      n1 = 0.0; 
    } else { 
      t1 *= t1; 
      n1 = t1 * t1 * dot3(GRAD3[gi1 as usize], x1, y1, z1); 
    } 

    let mut t2 = 0.6 - x2*x2 - y2*y2 - z2*z2; 
    if t2 < 0.0 { 
      n2 = 0.0; 
    }
    else { 
      t2 *= t2; 
      n2 = t2 * t2 * dot3(GRAD3[gi2 as usize], x2, y2, z2); 
    } 

    let mut t3 = 0.6 - x3*x3 - y3*y3 - z3*z3; 
    if t3 < 0.0 {
      n3 = 0.0; 
    } else { 
      t3 *= t3; 
      n3 = t3 * t3 * dot3(GRAD3[gi3 as usize], x3, y3, z3); 
    } 

    // Add contributions from each corner to get the final noise value. 
    // The result is scaled to stay just inside [-1,1] 
    return 32.0*(n0 + n1 + n2 + n3); 

  }

}