// ISIN (International Securities Identification Number), ISO 6166. Twelve
// characters: a two-letter country code, nine alphanumeric characters naming
// the security, and one check digit. The check digit is a Luhn digit computed
// over the identifier after every letter has been expanded to its two-digit
// numeric value.

///|
/// Expand an alphanumeric identifier to its digit string: letters become
/// 10..35, digits stay as they are. Returns None on any other character.
fn isin_expanded(s : String) -> String? {
  let buf = StringBuilder()
  for c in s {
    match alnum_value(c) {
      None => return None
      Some(v) => buf.write_string(v.to_string())
    }
  }
  Some(buf.to_string())
}

///|
/// Validate a 12-character ISIN. Lowercase input is accepted.
pub fn isin_valid(s : String) -> Bool {
  let cs = chars_of(s)
  if cs.length() != 12 {
    return false
  }
  if !is_alpha(upper_ascii(cs[0])) || !is_alpha(upper_ascii(cs[1])) {
    return false
  }
  if !alnum_only(s) {
    return false
  }
  match isin_expanded(s) {
    None => false
    Some(digits) => luhn_valid(digits)
  }
}

///|
/// The Luhn check digit implied by the first eleven characters of an ISIN.
pub fn isin_check_digit(first11 : String) -> Int? {
  let cs = chars_of(first11)
  if cs.length() != 11 || !alnum_only(first11) {
    return None
  }
  match isin_expanded(first11) {
    None => None
    Some(digits) => luhn_check_digit(digits)
  }
}

///|
/// The two-letter country code of an ISIN, or None when it is malformed.
pub fn isin_country(s : String) -> String? {
  let cs = chars_of(s)
  if cs.length() < 2 {
    return None
  }
  if !is_alpha(upper_ascii(cs[0])) || !is_alpha(upper_ascii(cs[1])) {
    return None
  }
  Some(char_str(upper_ascii(cs[0])) + char_str(upper_ascii(cs[1])))
}