use crate::coords::{Axial, Cube};

// Implementing this trait will give you the `Neighbours` trait, giving an
// iterator over the neighbours.
pub trait Neighbour {
  fn get_neighbour(&self, n: u8) -> Option<Self> where Self: Sized;
}

pub trait Neighbours {
  fn neighbours(&self) -> NeighbourIterator<Self> where Self: Sized;
}
pub struct NeighbourIterator<'a, C> {
  coord: &'a C,
  count: u8,
}

impl <'a, C> Iterator for NeighbourIterator<'a, C> where C: Neighbour {
  type Item = C;

  fn next(&mut self) -> Option<C> {
    self.count += 1;
    self.coord.get_neighbour(self.count - 1)
  }
}

impl <C> Neighbours for C where C: Neighbour {
  fn neighbours(&self) -> NeighbourIterator<Self> {
      NeighbourIterator{coord: self, count: 0}
  }
}

impl Neighbour for Axial {
  fn get_neighbour(&self, n: u8) -> Option<Axial> {
    match n {
      0 => Some(Axial(self.0, self.1 + 1)),
      1 => Some(Axial(self.0 + 1, self.1)),
      2 => Some(Axial(self.0 + 1, self.1 - 1)),
      3 => Some(Axial(self.0, self.1 - 1)),
      4 => Some(Axial(self.0 - 1, self.1)),
      5 => Some(Axial(self.0 - 1, self.1 + 1,)),
      _ => None
    }
  }
}

impl Neighbour for Cube {
  fn get_neighbour(&self, n: u8) -> Option<Cube> {
    match n {
      0 => Some(Cube(self.0, self.1 + 1, self.2 - 1)),
      1 => Some(Cube(self.0 + 1, self.1, self.2 - 1)),
      2 => Some(Cube(self.0 + 1, self.1 - 1, self.2)),
      3 => Some(Cube(self.0, self.1 - 1, self.2 + 1)),
      4 => Some(Cube(self.0 - 1, self.1, self.2 + 1)),
      5 => Some(Cube(self.0 - 1, self.1 + 1, self.2)),
      _ => None
    }
  }
}
