use std::{path::Iter, str::FromStr};

use num::Integer;
/// Number sequence, represents a set of integers as a union of several ranges
/// WARNING: this is intended for use in tests, so overlapping are not checked, also the ranges are not necessarily sorted
pub struct NumberSequence<T: Integer> {
    cur_range_index: usize,
    delta_in_range: T,
    ranges: Vec<(T, T)>,
}

impl<T: Integer + Copy + FromStr> NumberSequence<T> {
    pub fn from_str(s: &str) -> Option<Self> {
        let mut ranges = vec![];
        let groups = s.split(',');
        for group in groups {
            if group.is_empty() {
                continue;
            }
            if let Ok(num) = group.parse::<T>() {
                ranges.push((num, num + T::one()));
            }
            else if let Some((start_str, end_str)) = group.split_once('-') {
                if let (Ok(start), Ok(end)) = (start_str.parse::<T>(), end_str.parse::<T>()) {
                    ranges.push((start, end + T::one()));
                } else {
                    return None;
                }
            } else {
                return None;
            }
        }
        Some(Self { cur_range_index: 0, delta_in_range: T::zero(), ranges })
    }
}

impl<T: Integer + Copy> Iterator for NumberSequence<T> {
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        if self.cur_range_index >= self.ranges.len() {
            return None;
        }
        let (start, end) = self.ranges[self.cur_range_index];
        let delta = self.delta_in_range;
        if start + delta < end {
            self.delta_in_range = self.delta_in_range + T::one();
            Some(start + delta)
        } else {
            self.cur_range_index += 1;
            self.delta_in_range = T::zero();
            self.next()
        }
    }
}