//! This file models a combined sequences.

use super::*;
use crate::sequence::Sequence;
use crate::sequence::builder::SequenceBuilder;
use crate::sequence::renderer::RenderDisplay;
use std::convert::From;
use std::convert::TryFrom;
use std::fmt::Display;

#[derive(Default, Debug, Clone)]
pub struct CombinedSequence {
	inner: Vec<Sequence>
}

impl CombinedSequence {
	pub fn new() -> Self {
		Self::default()
	}
	pub fn push(&mut self, seq: Sequence) {
		self.inner.push(seq)
	}
}

impl Iterator for CombinedSequence {
	type Item = CombinedSequence;

	fn next(&mut self) -> Option<CombinedSequence> {
		for i in &mut self.inner {
			if let Some(_) = i.next() {
				return Some(self.clone())
			}
			else {
				i.rollover();
			}
		}
		return None
	}
}

impl Display for CombinedSequence {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		for i in self.inner.iter().rev() {
			write!(f, "{}", i )?
		}
		Ok(())
	}
}

impl From<Vec<Sequence>> for CombinedSequence {
	fn from(vec: Vec<Sequence>) -> Self {
		Self { inner: vec }
	}
}

impl TryFrom<&str> for CombinedSequence {
	type Error = SequenceError;


	/// ```rust
	/// use letter_sequence::sequence::combined::CombinedSequence;
	/// use std::convert::TryFrom;
	///
	/// let mut seq = CombinedSequence::try_from("A9Z").unwrap();
	/// assert_eq!( seq.next().unwrap().to_string(), "B0A" );
	/// assert_eq!( seq.next().unwrap().to_string(), "B0B" );
	/// 
	/// let mut seq = CombinedSequence::try_from("Z9Z").unwrap();
	/// assert_eq!( seq.next().unwrap().to_string(), "AA0A" );
	/// 
	/// let mut seq = CombinedSequence::try_from("9ZZ").unwrap();
	/// assert_eq!( seq.next().unwrap().to_string(), "10A" );
	/// // 0 based- 26 letters in the alphabet
	/// seq.nth( 26usize.pow(2) - 1 + 25 );
	/// assert_eq!( seq.to_string(), "10ZZ" );
	/// ```
	fn try_from(input: &str) -> Result<CombinedSequence, Self::Error> {
		let mut input : &mut str = &mut input.to_owned();
		if ! input.is_ascii() {
			return Err(SequenceError::InvalidString);
		}

		let mut splits: Vec<(usize, RenderDisplay)> = Vec::new();
		let mut iter = input.char_indices();
		while let Some((i, val)) = iter.next() {
			let val = RenderDisplay::try_from(val)?;
			match splits.last() {
				// current character is rendered differently from last character
				Some((_, lastval)) if *lastval != val => splits.push( (i,val) ),
				// first character seen
				None => splits.push( (i,val) ),
				_ => ()
			}
		}

		let mut seqs = CombinedSequence::new();
		while let Some((idx, _)) = splits.pop() {
			let (init, last) = input.split_at_mut(idx);
			input = &mut(*init);
			let mut seq = SequenceBuilder::try_from(&*last).unwrap();
			if ! splits.is_empty() {
				let length = last.len();
				seq = seq.max_render_capacity(length as u8);
			}
			let seq = seq.build()?;
			seqs.push( seq );
		}
		Ok(seqs)
	}
}
