use crate::{distance::Distance, lerp::LinearInterpolation};

pub trait GridLine {
  fn grid_line(&self, to: &Self) -> Vec<Self> where Self: Sized;
}

// This trait is automatically implemented for structs that implement the 
// `LinearInterpolation` and `Distance` traits.
impl <T> GridLine for T where T: LinearInterpolation, T: Distance {
  fn grid_line(&self, to: &T) -> Vec<Self> where T: Sized {
    let dist = self.dist(to);
    (0..=dist)
      .map(|n| self.lerp(to, n as f32 / dist as f32))
      .collect()
  }
}

#[cfg(test)]
mod test {
  use crate::coords::*;
  use crate::grid_line::*;

  #[test]
  fn grid_line_case_0() {
    let a = Cube(0, 0, 0);
    let b = Cube(3, -2, -1);
    verify_grid_line(&a.grid_line(&b), &a, &b);
  }

  #[test]
  fn grid_line_case_1() {
    let a = Cube(-4, 4, 0);
    let b = Cube(4, -4, 0);
    verify_grid_line(&a.grid_line(&b), &a, &b);
  }

  fn verify_grid_line(path: &Vec<Cube>, from: &Cube, to: &Cube) {
    // Assert that the path starts at `from`.
    assert_eq!(from, path.first().unwrap());
    // Assert that the path ends at `to`.
    assert_eq!(to, path.last().unwrap());

    // Assert that all subsequent coordinates have distance 1.
    let mut prev = path.first().unwrap();
    for x in path[1..].iter() {
      assert_eq!(1, x.dist(prev));
      prev = x;
    }

    // Assert that the line takes as many steps as the in-grid distance.
    assert_eq!(path.len(), from.dist(to) as usize + 1);
  }
}
