use super::*;

/// Converts any interger into base26 for alphabetic representation
/// Tested against
///  perl -E'my $x = "A"; say "$_ " . $x++ for 0 .. 900'
/// ```
/// use letter_sequence::sequence::helper;
/// const A: u64 = 0;
/// const Z: u64 = 25;
///
/// assert_eq!( helper::int_to_string(A).unwrap(),       "A");
/// assert_eq!( helper::int_to_string(Z).unwrap(),       "Z");
/// assert_eq!( helper::int_to_string(Z + 1).unwrap(),   "AA");
/// assert_eq!( helper::int_to_string(Z + 2).unwrap(),   "AB");
/// assert_eq!( helper::int_to_string(28).unwrap(),      "AC");
/// assert_eq!( helper::int_to_string(900).unwrap(),     "AHQ");
/// assert_eq!( helper::int_to_string(1000000).unwrap(), "BDWGO");
/// ```
pub fn int_to_string(i: u64) -> Result<String, SequenceError> {
	let (quot, rem) = (i / (LETTERS + 1), i % (LETTERS + 1));
	if quot == 0 {
		return Ok(String::from(int_to_char(rem as u32)?))
	}
	return Ok(
		format!(
			"{}{}",
			// We do this because int_to_char is 0-based.
			// But, 26 // 26 == 1; 26 % 26 == 1 (and we want that to be A)
			int_to_string(quot - 1)?,
			String::from(int_to_char(rem as u32)?),
		)
	)
}

/// Accepts 0-25 Ordinal number and converts it to char
/// ```
/// use letter_sequence::sequence::helper;
/// assert_eq!( helper::int_to_char(0).unwrap(),  'A' );
/// assert_eq!( helper::int_to_char(25).unwrap(), 'Z' );
/// ```
pub fn int_to_char(i: u32) -> Result<char, SequenceError> {
	const ASCII_A_OFFSET: u32 = 'A' as u32;
	if i > LETTERS as u32 {
		return Err(SequenceError::NotAsciiCharacter)
	}
	let letter = std::char::from_u32(i + ASCII_A_OFFSET)
		.unwrap();
	Ok(letter)
}


/// Accepts a character, and returns a representation of the ordinal position
/// ```
/// use letter_sequence::sequence::helper;
/// assert_eq!( helper::char_to_int('A').unwrap(), 0 );
/// assert_eq!( helper::char_to_int('B').unwrap(), 1 );
/// assert_eq!( helper::char_to_int('C').unwrap(), 2 );
/// assert_eq!( helper::char_to_int('Z').unwrap(), 25 );
/// ```
pub fn char_to_int(c: char) -> Result<u8, SequenceError> {
	if c.is_ascii_alphabetic() {
		Ok(c.to_ascii_uppercase() as u8 - 'A' as u8)
	}
	else {
		Err(SequenceError::InvalidCharacter(c))
	}
}

/// Accepts a character, and returns a representation of the ordinal position
/// ```
/// use letter_sequence::sequence::helper;
/// assert_eq!( helper::string_to_int("A").unwrap(), 0  );
/// assert_eq!( helper::string_to_int("Z").unwrap(), 25 );
/// assert_eq!( helper::string_to_int("AA").unwrap(), 26 );
/// use letter_sequence::SequenceError;
/// assert_eq!( helper::string_to_int(",").unwrap_err(), SequenceError::InvalidCharacter(',') );
/// ```
pub fn string_to_int(s: &str) -> Result<u64, SequenceError> {
	let mut max: u64 = 0;
	for (i,val) in s.chars().rev().enumerate() {
		let ord = char_to_int(val)? as u64;
		max += (1+ord) * 26u64.pow(i as u32);
	}
	Ok(max - 1)
}

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

	mod char {
		use super::*;
		#[test]
		fn simple() {
			for c in 'A'..'Z' {
				assert_eq!(int_to_char(c as u32 - 'A' as u32).unwrap(), c);
			}
			assert_eq!(
				int_to_char('Z' as u32 + 1).unwrap_err(),
				SequenceError::NotAsciiCharacter
			)
		}
	}

}
