use crate::sequence::helper::*;
use std::convert::From;
use std::convert::TryFrom;
use super::*;

/// ```rust
/// use letter_sequence::sequence::builder::SequenceBuilder;
/// use std::convert::TryFrom;
///
/// let mut seq = SequenceBuilder::try_from("Z")
///   .unwrap()
/// 	.upper()
/// 	.build()
/// 	.unwrap();
/// assert_eq!( seq.to_string(), "Z" );
/// seq.next();
/// assert_eq!( seq.to_string(), "AA" );
/// seq += 5u64;
/// assert_eq!( seq.to_string(), "AF" );
/// ```
///
/// This time with .max_render_capacity() set, and lowercase
/// ```rust
/// use letter_sequence::sequence::builder::SequenceBuilder;
/// use std::convert::TryFrom;
///
/// let mut seq = SequenceBuilder::try_from('y')
///   .unwrap()
/// 	.lower()
/// 	.max_render_capacity(1)
/// 	.build()
/// 	.unwrap();
/// assert_eq!( seq.next().unwrap().to_string(), "z"  );
/// assert_eq!( seq.to_string(),                 "z"  );
/// // Without .max_render_capacity(1) this would reutrn "aa"
/// assert_eq!( seq.next(),                      None );
/// ```
///
/// Also boring number sequence
/// ```rust
/// use letter_sequence::sequence::builder::SequenceBuilder;
/// use std::convert::TryFrom;
///
/// let mut seq = SequenceBuilder::from(102)
/// 	.numeric()
/// 	.build()
/// 	.unwrap();
/// assert_eq!( seq.next().unwrap().to_string(), "103"  );
/// assert_eq!( seq.to_string(),                 "103"  );
/// ```
#[derive(Debug, Default)]
pub struct SequenceBuilder {
	start: u64,
	renderer: SeqRendererBuilder,
	length: Option<u8>,
}

impl SequenceBuilder {
	/// Creates a new Sequence starting at `start` (0-base)
	/// ```
	/// use letter_sequence::sequence::builder;
	///
	/// let seq = builder::SequenceBuilder::new(2).upper().build().unwrap();
	/// assert_eq!( seq.to_string(), "C" );
	///
	/// let seq = builder::SequenceBuilder::new(5).build().unwrap();
	/// assert_eq!( seq.to_string(), "5" );
	/// ```
	pub fn new(start: u64) -> Self {
		Self { start, ..Default::default() }
	}
	/// Limits the chacters in rendered output, incrementing past limit generates
	/// error. default: unlimited
	pub fn max_render_capacity(mut self, chars: u8) -> Self {
		self.renderer = self.renderer.capacity(chars);
		self
	}
	/// Limits the minimum width of the rendered output, this only works on
	/// Numeric which effectively zero-pads the output.
	pub fn length(mut self, chars: u8) -> Self {
		self.length = Some(chars);
		self
	}
	/// Render the sequence in lowercase
	pub fn lower(mut self) -> Self {
		self.renderer = self.renderer.lower();
		self
	}
	/// Render the sequence in uppercase, default
	pub fn upper(mut self) -> Self {
		self.renderer = self.renderer.upper();
		self
	}
	/// Render the sequence in digits
	pub fn numeric(mut self) -> Self {
		self.renderer = self.renderer.numeric();
		self
	}

	pub fn build(self) -> Result<Sequence, SequenceError> {
		let renderer = self.renderer.build()?;
		match (renderer.display(), self.length) {
			(RenderDisplay::Upper | RenderDisplay::Lower, Some(_)) => return Err(SequenceError::PaddingAlpha),
			_ => ()
		};
		let mut seq = Sequence {
			inner: 0,
			renderer,
			.. Default::default()
		};
		seq.set(self.start)?;
		Ok(seq)
	}
}

impl From<u64> for SequenceBuilder {
	fn from(value: u64) -> Self {
		Self::new(value)
	}
}

/// ```rust
/// use letter_sequence::sequence::builder::SequenceBuilder;
/// use std::convert::TryFrom;
///	assert_eq!(
///		SequenceBuilder::try_from("00001")
///			.unwrap()
///			.build()
///			.unwrap()
///			.to_string(),
///		"1"
///	);
///	assert_eq!(
///		SequenceBuilder::try_from("00001")
///			.unwrap()
///			.upper()
///			.build()
///			.unwrap()
///			.to_string(),
///		"B"
///	);
/// ```
impl TryFrom<&str> for SequenceBuilder {
	type Error = SequenceError;
	fn try_from(value: &str) -> Result<Self, Self::Error> {
		let first_char = value.chars().next().unwrap();
		match renderer::RenderDisplay::try_from(first_char)? {
			renderer::RenderDisplay::Numeric => Ok( Self::new(value.parse::<u64>().unwrap()).numeric() ),
			renderer::RenderDisplay::Upper => Ok( Self::new(string_to_int(&value)?).upper() ),
			renderer::RenderDisplay::Lower => Ok( Self::new(string_to_int(&value)?).lower() )
		}
	}
}

impl TryFrom<char> for SequenceBuilder {
	type Error = SequenceError;
	fn try_from(value: char) -> Result<Self, Self::Error> {
		Ok(Self::new(char_to_int(value)? as u64))
	}
}

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

	/// Test violations of the max_render_capacity system, ensure we return an error
	/// Test that we can increment past respective .max_numeric and .max_alpha
	#[test]
	fn max_render_capacity() {
		// Test link between max_alpha(chars) and the OutOfRange
		for limit in 1..10 {
			let max = crate::sequence::renderer::max_alpha(limit);
			let seq = SequenceBuilder::new(max).upper().max_render_capacity(limit);
			let mut seq = seq.build().unwrap();
			let render_as = "Z".repeat(limit as usize);
			assert_eq!( seq.to_string(), render_as );
			assert_eq!(
				seq.inc().unwrap_err(),
				SequenceError::OutOfRange,
			);
			assert_eq!( seq.next(), None )
		}

		// Test max_numeric(chars) + 1 throws OutOfRange
		for limit in 1..10 {
			let max = crate::sequence::renderer::max_numeric(limit);
			let mut seq = SequenceBuilder::new(max).numeric().max_render_capacity(limit).build().unwrap();
			let render_as = "9".repeat(limit as usize);
			assert_eq!( seq.to_string(), render_as, "Render {} as {}", max, render_as );
			assert_eq!(
				seq.inc().unwrap_err(),
				SequenceError::OutOfRange,
				"Throw error with numeric char limit of {} which can only represent {}",
					limit,
					max
			);
			assert_eq!( seq.next(), None )
		}
	}

	#[test]
	fn simple() {
		for c in 'A'..'Z' {
			let seq = SequenceBuilder::new(c as u64 - 'A' as u64).upper().build().unwrap();
			assert_eq!(
				seq.to_string(),
				c.to_string(),
			);

			let seq = SequenceBuilder::new(c as u64 - 'A' as u64).lower().build().unwrap();
			assert_eq!(
				seq.to_string(),
				c.to_string().to_lowercase(),
			);
		}
		for i in 1..123_456 {
			assert_eq!(
				SequenceBuilder::new(i).numeric().build().unwrap().to_string(),
				i.to_string()
			);
		}
	}

	#[test]
	fn rollover() {
		for i in A..Z {

			// Easy check for A[A-Z] after Z
			let seq = SequenceBuilder::new(Z as u64).upper().build().unwrap();
			let result = format!("A{}", std::char::from_u32('A' as u32 + i).unwrap());
			assert_eq!(
				seq.skip(i as usize).next().unwrap().to_string(),
				result
			);

			
			let mut seq = SequenceBuilder::new(Z as u64).upper().build().unwrap();
			assert_eq!(
				seq.nth(i as usize).unwrap().to_string(),
				result,
				".nth({})", i
			);

			let seq = SequenceBuilder::new(Z as u64 + 1 + i as u64).lower().build().unwrap();
			assert_eq!(
				seq.to_string(),
				result.to_lowercase()
			);
			
			let mut seq = SequenceBuilder::new(A as u64).upper().build().unwrap();
			for len in 1..5 {
				assert_eq!(
					seq.nth(26usize.pow(len as u32) - 1).unwrap().to_string(),
					"A".repeat(len + 1),
				);
			}
			
			let mut seq = SequenceBuilder::new(Z as u64).upper().build().unwrap();
			for len in 2..5 {
				assert_eq!(
					seq.nth(26usize.pow(len as u32) - 1).unwrap().to_string(),
					"Z".repeat(len),
				);
			}
		}
	}

}
