use crate::data::number::SimpleNumber;
use garnish_lang_traits::GarnishNumber;

/// Custom size iterator for [`garnish_lang_traits::GarnishData`] implementation.
pub struct SizeIterator {
    min: usize,
    max: usize,
    current_front: usize,
    current_back: usize,
}

impl SizeIterator {
    pub fn new(min: usize, max: usize) -> Self {
        Self {
            min,
            max,
            current_front: min,
            current_back: max,
        }
    }

    pub fn reset(&mut self) {
        self.current_front = self.min;
        self.current_back = self.max;
    }
}

impl Iterator for SizeIterator {
    type Item = usize;

    fn next(&mut self) -> Option<Self::Item> {
        if self.current_front >= self.current_back {
            return None;
        }

        self.current_front += 1;
        return Some(self.current_front - 1);
    }
}

impl DoubleEndedIterator for SizeIterator {
    fn next_back(&mut self) -> Option<Self::Item> {
        if self.current_back == 0 || self.current_back <= self.current_front {
            return None;
        }

        self.current_back -= 1;
        return Some(self.current_back);
    }
}

/// Custom size iterator for [`garnish_lang_traits::GarnishData`] implementation.
pub struct NumberIterator {
    min: SimpleNumber,
    max: SimpleNumber,
    current_front: SimpleNumber,
    current_back: SimpleNumber,
}

impl NumberIterator {
    pub fn new(min: SimpleNumber, max: SimpleNumber) -> Self {
        Self {
            min,
            max,
            current_front: min,
            current_back: max,
        }
    }

    pub fn reset(&mut self) {
        self.current_front = self.min;
        self.current_back = self.max;
    }
}

impl Iterator for NumberIterator {
    type Item = SimpleNumber;

    fn next(&mut self) -> Option<Self::Item> {
        if self.current_front >= self.current_back {
            return None;
        }

        self.current_front = self.current_front.plus(SimpleNumber::Integer(1)).unwrap_or(self.max);
        return Some(self.current_front.subtract(SimpleNumber::Integer(1)).unwrap_or(self.min));
    }
}

impl DoubleEndedIterator for NumberIterator {
    fn next_back(&mut self) -> Option<Self::Item> {
        if self.current_back == SimpleNumber::Integer(0) || self.current_back <= self.current_front {
            return None;
        }

        self.current_back = self.current_back.subtract(SimpleNumber::Integer(1)).unwrap_or(self.min);
        return Some(self.current_back);
    }
}

#[cfg(test)]
mod tests {
    use crate::data::SizeIterator;

    #[test]
    fn forward_iteration_full() {
        let mut iter = SizeIterator::new(0, 5);

        assert_eq!(iter.next(), Some(0));
        assert_eq!(iter.next(), Some(1));
        assert_eq!(iter.next(), Some(2));
        assert_eq!(iter.next(), Some(3));
        assert_eq!(iter.next(), Some(4));
        assert_eq!(iter.next(), None);
    }

    #[test]
    fn backward_iteration_full() {
        let mut iter = SizeIterator::new(0, 5);

        assert_eq!(iter.next_back(), Some(4));
        assert_eq!(iter.next_back(), Some(3));
        assert_eq!(iter.next_back(), Some(2));
        assert_eq!(iter.next_back(), Some(1));
        assert_eq!(iter.next_back(), Some(0));
        assert_eq!(iter.next_back(), None);
    }

    #[test]
    fn front_passes_back() {
        let mut iter = SizeIterator::new(0, 5);

        assert_eq!(iter.next(), Some(0));
        assert_eq!(iter.next(), Some(1));
        assert_eq!(iter.next(), Some(2));
        assert_eq!(iter.next_back(), Some(4));
        assert_eq!(iter.next_back(), Some(3));
        assert_eq!(iter.next(), None);
        assert_eq!(iter.next_back(), None);
    }

    #[test]
    fn back_passes_front() {
        let mut iter = SizeIterator::new(0, 5);

        assert_eq!(iter.next_back(), Some(4));
        assert_eq!(iter.next_back(), Some(3));
        assert_eq!(iter.next_back(), Some(2));
        assert_eq!(iter.next(), Some(0));
        assert_eq!(iter.next(), Some(1));
        assert_eq!(iter.next(), None);
        assert_eq!(iter.next_back(), None);
    }

    #[test]
    fn reset() {
        let mut iter = SizeIterator::new(0, 5);

        assert_eq!(iter.next_back(), Some(4));
        assert_eq!(iter.next_back(), Some(3));
        assert_eq!(iter.next_back(), Some(2));
        assert_eq!(iter.next(), Some(0));
        assert_eq!(iter.next(), Some(1));
        assert_eq!(iter.next(), None);
        assert_eq!(iter.next_back(), None);

        iter.reset();

        assert_eq!(iter.next_back(), Some(4));
        assert_eq!(iter.next_back(), Some(3));
        assert_eq!(iter.next_back(), Some(2));
        assert_eq!(iter.next(), Some(0));
        assert_eq!(iter.next(), Some(1));
        assert_eq!(iter.next(), None);
        assert_eq!(iter.next_back(), None);
    }
}
