use crate::{CodeType, MongolConvertError, Warning};
use std::borrow::Cow;
const SUFFIXES: &[&str] = &[
"ᠶᠢᠨ",
"ᠤᠨ",
"ᠦᠨ",
"ᠤ",
"ᠦ", "ᠶᠢ",
"ᠢ", "ᠳᠤ",
"ᠳᠦ",
"ᠲᠤ",
"ᠲᠦ",
"ᠳᠤᠷ",
"ᠳᠦᠷ",
"ᠲᠤᠷ",
"ᠲᠦᠷ", "ᠠᠴᠠ",
"ᠡᠴᠡ", "ᠢᠶᠠᠷ",
"ᠢᠶᠡᠷ", "ᠢᠶᠠᠨ",
"ᠢᠶᠡᠨ", "ᠯᠤᠭ\u{180E}ᠠ",
"ᠯᠦᠭᠡ", "ᠨᠤᠭᠤᠳ",
"ᠨᠦᠭᠦᠳ",
"ᠤᠳ",
"ᠦᠳ", "ᠳᠠᠭᠠᠨ",
"ᠳᠡᠭᠡᠨ",
"ᠲᠠᠭᠠᠨ",
"ᠲᠡᠭᠡᠨ", "ᠶᠤᠭᠠᠨ",
"ᠶᠦᠭᠡᠨ", "ᠠᠴᠠᠭᠠᠨ",
"ᠡᠴᠡᠭᠡᠨ", "ᠳᠤᠨᠢ",
"ᠳᠦᠨᠢ",
"ᠲᠤᠨᠢ",
"ᠲᠦᠨᠢ", "ᠳᠠᠬᠢ",
"ᠳᠡᠬᠢ",
"ᠲᠠᠬᠢ",
"ᠲᠡᠬᠢ", "ᠲᠡᠬᠡᠨ", "ᠪᠠᠷ",
"ᠪᠡᠷ", "ᠪᠠᠨ",
"ᠪᠡᠨ", "ᠲᠠᠢ",
"ᠲᠡᠢ", "ᠨᠠᠷ",
"ᠨᠡᠷ", "ᠳᠤᠭᠠᠷ",
"ᠳᠦᠭᠡᠷ", ];
fn letter(c: char) -> bool {
matches!(c, '\u{1820}'..='\u{1842}')
}
fn word_char(c: char) -> bool {
letter(c) || matches!(c, '\u{180B}'..='\u{180F}' | '\u{200C}' | '\u{200D}')
}
fn mongolian_word(s: &str) -> bool {
s.chars().all(word_char) && s.chars().any(letter)
}
const T_SELECTING: &str = "ᠪᠭᠬᠷᠰᠱᠳᠲᠴᠺᠫᠹᠽᠼᠾ";
fn token_end(c: char) -> bool {
c.is_alphanumeric()
|| matches!(
c,
')' | ']'
| '}'
| '"'
| '\''
| '\u{2019}'
| '\u{201D}'
| '\u{00BB}'
| '\u{203A}'
| '\u{300B}'
| '\u{300D}'
| '\u{300F}'
| '\u{3009}'
| '\u{3011}'
| '\u{3015}'
| '\u{FF09}'
| '\u{FF3D}'
| '\u{FF5D}'
| '\u{FE36}'
| '\u{FE38}'
| '\u{FE3A}'
| '\u{FE3C}'
| '\u{FE3E}'
| '\u{FE40}'
| '\u{FE42}'
| '\u{FE44}'
| '%'
| '\u{FF05}'
| '\u{2030}'
| '\u{00B0}'
| '\u{2103}'
| '\u{2109}'
| '+'
| '$'
| '\u{00A5}'
| '\u{20AC}'
| '\u{00A3}'
| '\u{FFE5}'
| '#'
| '\u{2116}'
)
}
fn suffix_context(previous: &str, suffix: &str) -> bool {
let last = previous.chars().rev().find(|&c| letter(c));
match suffix {
"ᠳᠤᠭᠠᠷ" | "ᠳᠦᠭᠡᠷ" => false,
"ᠲᠠᠢ" | "ᠲᠡᠢ" => true,
_ if suffix.starts_with('ᠲ') => last.is_some_and(|c| T_SELECTING.contains(c)),
"ᠪᠠᠷ" | "ᠪᠡᠷ" | "ᠪᠠᠨ" | "ᠪᠡᠨ" => {
last.is_some_and(|c| c <= '\u{1827}' || c == 'ᠶ')
}
_ => true,
}
}
pub(crate) fn suffix_separators(
from: CodeType,
input: &str,
) -> Result<(Cow<'_, str>, Vec<Warning>), MongolConvertError> {
if !matches!(from, CodeType::MenkLetter | CodeType::Delehi) {
return Err(MongolConvertError::UnsupportedInputRepair(from));
}
let mut out = String::new();
let mut warnings = Vec::new();
let mut copied = 0;
let mut word_start = 0;
for (offset, c) in input.char_indices() {
if matches!(c, ' ' | '\u{00A0}') {
let next = offset + c.len_utf8();
let previous = &input[word_start..offset];
let word = mongolian_word(previous);
let other_token =
previous.is_empty() && input[..offset].chars().next_back().is_some_and(token_end);
let suffix = (word || other_token)
&& SUFFIXES.iter().any(|suffix| {
input[next..].strip_prefix(suffix).is_some_and(|rest| {
(!word || suffix_context(previous, suffix))
&& rest.chars().next().map_or(true, |c| {
c.is_whitespace()
|| matches!(
c,
'\u{1800}'
..='\u{1809}'
| '.'
| ','
| ';'
| ':'
| '!'
| '?'
| ')'
| ']'
| '}'
| '"'
| '\''
)
})
})
});
if suffix {
out.push_str(&input[copied..offset]);
out.push('\u{202F}');
copied = next;
warnings.push(Warning::RepairedSuffixSeparator {
byte_offset: offset,
original: c,
});
}
}
if !word_char(c) {
word_start = offset + c.len_utf8();
}
}
if warnings.is_empty() {
Ok((Cow::Borrowed(input), warnings))
} else {
out.push_str(&input[copied..]);
Ok((Cow::Owned(out), warnings))
}
}