// External includes.

// Standard includes.

// Internal includes.
use super::{Position, Shape};

/// Iterates all valid local positions of a `Shape`.
pub struct ShapeIterator<'a> {
    local_pos_x: u32,
    local_pos_y: u32,
    target: &'a dyn Shape,
}

impl<'a> ShapeIterator<'a> {
    /// Creates a new `ShapeIterator`.
    pub fn new(target: &'a dyn Shape) -> Self {
        Self {
            local_pos_x: 0,
            local_pos_y: 0,
            target,
        }
    }
}

impl<'a> Iterator for ShapeIterator<'a> {
    type Item = Position;

    fn next(&mut self) -> Option<Position> {
        // Only a problem on implausibly large maps.
        #[allow(clippy::cast_possible_wrap)]
        let mut local_pos = Position::new(self.local_pos_x as i32, self.local_pos_y as i32);
        #[allow(clippy::cast_possible_wrap)]
        while !self.target.intersects_local_position(local_pos) {
            if self.local_pos_x >= self.target.width() {
                self.local_pos_y += 1;
                self.local_pos_x = 0;
            }

            if self.local_pos_y >= self.target.height() {
                return None;
            }

            self.local_pos_x += 1;

            local_pos = Position::new(self.local_pos_x as i32, self.local_pos_y as i32);
        }

        let output = {
            if self.target.intersects_local_position(local_pos) {
                Some(local_pos)
            } else {
                None
            }
        };

        self.local_pos_x += 1;

        output
    }
}
