pub mod helper;
pub mod builder;
pub mod renderer;
pub mod combined;
use crate::sequence::renderer::*;
use std::fmt;
use super::*;

#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct Sequence {
	inner: u64,
	capacity: u64,
	length: Option<u8>,
	renderer: SeqRenderer
}

impl Iterator for Sequence {
	type Item = Self;
	fn next(&mut self) -> Option<Self::Item> {
		self.inc().ok().and_then(|_| Some(self.clone()))
	}
}

impl fmt::Display for Sequence {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
		write!(f, "{}", self.renderer.render( self.inner ).unwrap() )
	}
}

impl Sequence {

	/// ```rust
	/// use letter_sequence::sequence::Sequence;
	/// assert_eq!( Sequence::new(42).unwrap().to_string(), "42" );
	/// ```
	pub fn new(x: u64) -> Result<Self, SequenceError> {
		let mut o = Self::default();
		o.set(x)?;
		Ok(o)
	}

	fn new_unchecked(x: u64) -> Self {
		Self { inner: x, .. Self::default() }
	}

	pub fn inc(&mut self) -> Result<(), SequenceError> {
		Sequence::add(self,1)
	}
	
	pub fn add(&mut self, rhs: u64) -> Result<(), SequenceError> {
		if let Some(limit) = self.renderer.max() {
			if limit < self.inner + rhs {
				return Err(SequenceError::OutOfRange)
			}
		}
		self.inner += rhs;
		Ok(())
	}

	fn rollover(&mut self) -> () {
		self.inner = Default::default();
	}

	pub fn set(&mut self, x: u64) -> Result<(), SequenceError> {
		if let Some(limit) = self.renderer.max() {
			if x > limit {
				return Err(SequenceError::OutOfRange)
			}
		}
		self.inner = x;
		Ok(())
	}
}

use std::ops::AddAssign;
impl<T: Into<u64>> AddAssign<T> for Sequence {
	fn add_assign(&mut self, rhs: T) {
		Sequence::add(self, rhs.into()).unwrap();
	}
}


#[cfg(test)]
mod test {
	use super::*;

	#[test]
	fn default() {
		assert_eq!( Sequence::default().to_string(), "0" );
	}

	#[test]
	fn new_unchecked() {
		assert_eq!( Sequence::new_unchecked(u64::MAX).to_string(), u64::MAX.to_string() );
	}
	
	#[test]
	fn num() {
		for i in 0..123_456 {
			let seq = Sequence::new(i as u64).unwrap();
			assert_eq!( seq.to_string(), i.to_string() );
		}
	}

	#[test]
	fn lower() {
		for i in 0..100 {
			let seq = Sequence {
				inner: i as u64,
				renderer: SeqRendererBuilder::default().lower().build().unwrap(),
				..Default::default()
			};
			assert_eq!(
				seq.to_string().matches( |c: char| {
					c.is_numeric() || c.is_whitespace() || c.is_control()
				} ).into_iter().collect::<Vec<&str>>()
				, Vec::<&str>::new()
			);
			assert_eq!(
				seq.to_string(),
				seq.to_string().to_ascii_lowercase(),
			)
		}
	}
	
	#[test]
	fn upper() {
		for i in 0..100 {
			let seq = Sequence {
				inner: i as u64,
				renderer: SeqRendererBuilder::default().upper().build().unwrap(),
				..Default::default()
			};
			assert_eq!(
				seq.to_string().matches( |c: char| {
					c.is_numeric() || c.is_whitespace() || c.is_control()
				} ).into_iter().collect::<Vec<&str>>()
				, Vec::<&str>::new()
			);
			assert_eq!(
				seq.to_string(),
				seq.to_string().to_ascii_uppercase(),
			)
		}
	}

	//inner: 1 * 26u64.pow(2) + 1 * 26u64.pow(1)  + 0 * 26u64.pow(0),
	#[test]
	fn rollover() {
		let mut seq = Sequence {
			inner: Z as u64,
			renderer: SeqRendererBuilder::default().upper().build().unwrap(),
			..Default::default()
		};
		assert_eq!( seq.next().unwrap().to_string(), "AA" );
		assert_eq!( seq.next().unwrap().to_string(), "AB" );
		
		let mut seq = Sequence {
			inner: Z as u64,
			renderer: SeqRendererBuilder::default().lower().build().unwrap(),
			..Default::default()
		};
		assert_eq!( seq.next().unwrap().to_string(), "aa" );
		assert_eq!( seq.next().unwrap().to_string(), "ab" );
		
		let mut seq = Sequence {
			inner: Z as u64,
			renderer: SeqRendererBuilder::default().numeric().build().unwrap(),
			..Default::default()
		};
		assert_eq!( seq.next().unwrap().to_string(), (Z + 1).to_string() );
		assert_eq!( seq.next().unwrap().to_string(), (Z + 2).to_string() );
	}

}
