use direction::Direction;
use grid_2d::*;
use search::*;
use std::slice;

pub(crate) trait PathNode {
    fn from_parent(&self) -> Option<Direction>;
    fn coord(&self) -> Coord;
}

pub(crate) fn make_path_all_adjacent<N: PathNode>(
    node_grid: &Grid<N>,
    goal_index: usize,
    path: &mut Vec<Direction>,
) {
    path.clear();
    let mut index = goal_index;
    while let Some(from_parent) = node_grid[index].from_parent() {
        path.push(from_parent);
        let offset = from_parent.opposite().coord();
        index = node_grid
            .index_of_coord(node_grid[index].coord() + offset)
            .expect("Invalid search state");
    }
    path.reverse();
}

pub(crate) fn make_path_jump_points<Cost>(
    node_grid: &Grid<SearchNode<Cost>>,
    goal_coord: Coord,
    seq: u64,
    path: &mut Vec<Direction>,
) {
    path.clear();

    let mut node = node_grid.get(goal_coord).expect("Invalid search state");

    loop {
        let from_parent = if let Some(from_parent) = node.from_parent() {
            from_parent
        } else {
            break;
        };

        path.push(from_parent);

        let step = from_parent.opposite().coord();
        let mut coord = node.coord;
        loop {
            coord += step;
            let next_node = node_grid.get(coord).expect("Invalid search state");
            if next_node.seen == seq {
                node = next_node;
                break;
            }

            path.push(from_parent);
        }
    }

    path.reverse();
}

pub struct PathWalk<'a> {
    current_coord: Coord,
    directions: slice::Iter<'a, Direction>,
}

impl<'a> PathWalk<'a> {
    pub fn new(start: Coord, path: &'a Vec<Direction>) -> Self {
        Self {
            current_coord: start,
            directions: path.iter(),
        }
    }
}

impl<'a> Iterator for PathWalk<'a> {
    type Item = (Coord, Direction);
    fn next(&mut self) -> Option<Self::Item> {
        if let Some(&direction) = self.directions.next() {
            let offset: Coord = direction.coord();
            self.current_coord = self.current_coord + offset;
            Some((self.current_coord, direction))
        } else {
            None
        }
    }
}
