include!(concat!(env!("OUT_DIR"), "/bidi_class.rs"));
use precis_core::Codepoints;
fn bidi_class_cp(cp: u32) -> BidiClass {
    match BIDI_CLASS_TABLE.binary_search_by(|(cps, _)| cps.partial_cmp(&cp).unwrap()) {
        Ok(idx) => BIDI_CLASS_TABLE[idx].1,
                        Err(_) => BidiClass::L,
    }
}
fn bidi_class(c: char) -> BidiClass {
    bidi_class_cp(c as u32)
}
pub fn has_rtl(label: &str) -> bool {
    label
        .find(|c| matches!(bidi_class(c), BidiClass::R | BidiClass::AL | BidiClass::AN))
        .is_some()
}
pub fn satisfy_bidi_rule(label: &str) -> bool {
    let mut it = label.chars();
    if let Some(c) = it.next() {
        let first = bidi_class(c);
                if matches!(first, BidiClass::R | BidiClass::AL) {
                        is_valid_rtl_label(it, first)
        } else if first == BidiClass::L {
                        is_valid_ltr_label(it, first)
        } else {
                        false
        }
    } else {
                true
    }
}
fn is_valid_rtl_label<I>(it: I, prev: BidiClass) -> bool
where
    I: IntoIterator<Item = char>,
{
    let mut prev = prev;
    let mut nsm = false;
    let mut en = false;
    let mut an = false;
    for c in it {
        let class = bidi_class(c);
                                match class {
            BidiClass::R
            | BidiClass::AL
            | BidiClass::ES
            | BidiClass::CS
            | BidiClass::ET
            | BidiClass::ON
            | BidiClass::BN => {}
            BidiClass::AN => {
                if en {
                                                            return false;
                }
                an = true;
            }
            BidiClass::EN => {
                if an {
                                                            return false;
                }
                en = true;
            }
            BidiClass::NSM => {
                                                                                if !matches!(
                    prev,
                    BidiClass::R | BidiClass::AL | BidiClass::EN | BidiClass::AN
                ) {
                                        return false;
                }
                nsm = true;
                continue;
            }
                        _ => return false,
        }
        if nsm {
                                                return false;
        } else {
            prev = class;
        }
    }
    nsm || matches!(
        prev,
        BidiClass::R | BidiClass::AL | BidiClass::EN | BidiClass::AN
    )
}
fn is_valid_ltr_label<I>(it: I, prev: BidiClass) -> bool
where
    I: IntoIterator<Item = char>,
{
    let mut prev = prev;
    let mut nsm = false;
    for c in it {
        let class = bidi_class(c);
                                match class {
            BidiClass::L
            | BidiClass::EN
            | BidiClass::ES
            | BidiClass::CS
            | BidiClass::ET
            | BidiClass::ON
            | BidiClass::BN => {
                if nsm {
                                                                                return false;
                }
                prev = class;
            }
            BidiClass::NSM => {
                                                                                if !matches!(prev, BidiClass::L | BidiClass::EN) {
                                        return false;
                }
                nsm = true;
            }
                        _ => return false,
        };
    }
    nsm || matches!(prev, BidiClass::L | BidiClass::EN)
}
#[cfg(test)]
mod bidi_tests {
    use crate::bidi::*;
    const L: char = '\u{00aa}';
    const R: char = '\u{05be}';
    const AL: char = '\u{0608}';
    const AN: char = '\u{06dd}';
    const EN: char = '\u{00b9}';
    const ES: char = '\u{002b}';
    const CS: char = '\u{002c}';
    const ET: char = '\u{058f}';
    const ON: char = '\u{037e}';
    const BN: char = '\u{00ad}';
    const NSM: char = '\u{1e2ae}';
    const WS: char = '\u{0020}';
    macro_rules! str_chars {
    ($($args:expr),*) => {{
		let mut result = String::from("");
		$(
			result.push($args);
		)*
		result
		}}
	}
    #[test]
    fn test_bidi_class() {
        assert_eq!(bidi_class(L), BidiClass::L);
        assert_eq!(bidi_class(R), BidiClass::R);
        assert_eq!(bidi_class(AL), BidiClass::AL);
        assert_eq!(bidi_class(AN), BidiClass::AN);
        assert_eq!(bidi_class(EN), BidiClass::EN);
        assert_eq!(bidi_class(ES), BidiClass::ES);
        assert_eq!(bidi_class(CS), BidiClass::CS);
        assert_eq!(bidi_class(ET), BidiClass::ET);
        assert_eq!(bidi_class(ON), BidiClass::ON);
        assert_eq!(bidi_class(BN), BidiClass::BN);
        assert_eq!(bidi_class(NSM), BidiClass::NSM);
        assert_eq!(bidi_class(WS), BidiClass::WS);
                        assert_eq!(bidi_class('\u{e0080}'), BidiClass::L);
    }
    #[test]
    fn test_has_rtl() {
        assert!(!has_rtl(""));
        assert!(!has_rtl("Hi"));
                assert!(has_rtl(&str_chars!(R)));
        assert!(has_rtl(&str_chars!(R, 'A')));
        assert!(has_rtl(&str_chars!('A', R)));
                assert!(has_rtl(&str_chars!(AL)));
        assert!(has_rtl(&str_chars!(AL, 'A')));
        assert!(has_rtl(&str_chars!('A', AL)));
                assert!(has_rtl(&str_chars!(AN)));
        assert!(has_rtl(&str_chars!(AN, 'A')));
        assert!(has_rtl(&str_chars!('A', AN)));
    }
    #[test]
    fn test_bidi_rule() {
                assert!(satisfy_bidi_rule(""));
                        assert!(satisfy_bidi_rule(&str_chars!(L)));
                assert!(satisfy_bidi_rule(&str_chars!(R)));
                assert!(satisfy_bidi_rule(&str_chars!(AL)));
                assert!(!satisfy_bidi_rule(&str_chars!(ES)));
                assert!(!satisfy_bidi_rule(&str_chars!(WS)));
    }
    #[test]
    fn test_rtl_label() {
                                assert!(satisfy_bidi_rule(&str_chars!(
            R, AL, ES, CS, ET, ON, BN, AN
        )));
        assert!(satisfy_bidi_rule(&str_chars!(
            R, AL, ES, CS, ET, ON, BN, EN
        )));
        assert!(satisfy_bidi_rule(&str_chars!(
            R, AL, ES, CS, ET, ON, BN, EN, NSM
        )));
                        assert!(!satisfy_bidi_rule(&str_chars!(
            R, AL, ES, CS, WS, ON, BN, EN, NSM
        )));
                                        assert!(satisfy_bidi_rule(&str_chars!(R, AL, EN, NSM, NSM)));
        assert!(satisfy_bidi_rule(&str_chars!(R, NSM, NSM, NSM, NSM)));
                assert!(!satisfy_bidi_rule(&str_chars!(R, CS)));
        assert!(!satisfy_bidi_rule(&str_chars!(R, ET, NSM)));
        assert!(!satisfy_bidi_rule(&str_chars!(R, BN, NSM, NSM)));
                        assert!(!satisfy_bidi_rule(&str_chars!(R, NSM, AN)));
        assert!(!satisfy_bidi_rule(&str_chars!(R, BN, NSM, NSM, AN)));
                                assert!(!satisfy_bidi_rule(&str_chars!(R, EN, CS, AN, AL, NSM)));
        assert!(!satisfy_bidi_rule(&str_chars!(R, AN, CS, EN, AL, NSM)));
                assert!(satisfy_bidi_rule(&str_chars!(R, AN, AN, AL)));
                assert!(satisfy_bidi_rule(&str_chars!(R, EN, EN, AL)));
    }
    #[test]
    fn test_ltr_label() {
                                assert!(satisfy_bidi_rule(&str_chars!(L, EN, ES, CS, ET, ON, BN, L)));
                        assert!(!satisfy_bidi_rule(&str_chars!(L, EN, ES, CS, R, ON, BN, L)));
                                        assert!(satisfy_bidi_rule(&str_chars!(L)));
        assert!(satisfy_bidi_rule(&str_chars!(L, EN)));
        assert!(satisfy_bidi_rule(&str_chars!(L, EN, NSM)));
        assert!(satisfy_bidi_rule(&str_chars!(L, EN, NSM, NSM)));
        assert!(satisfy_bidi_rule(&str_chars!(L, NSM)));
                        assert!(!satisfy_bidi_rule(&str_chars!(L, ES)));
        assert!(!satisfy_bidi_rule(&str_chars!(L, CS, NSM)));
                        assert!(!satisfy_bidi_rule(&str_chars!(L, NSM, EN)));
        assert!(!satisfy_bidi_rule(&str_chars!(L, NSM, NSM, L, EN, NSM)));
    }
}