use crate::{fmt_list, UnicodeData};
use std::fmt;

pub(crate) fn generate_case_mapping(data: &UnicodeData) -> String {
    let mut file = String::new();

    file.push_str(HEADER.trim_start());

    let decl_type = "&[(char, [char; 3])]";

    file.push_str(&format!(
        "static LOWERCASE_TABLE: {} = &[{}];",
        decl_type,
        fmt_list(data.to_lower.iter().map(to_mapping))
    ));
    file.push_str("\n\n");
    file.push_str(&format!(
        "static UPPERCASE_TABLE: {} = &[{}];",
        decl_type,
        fmt_list(data.to_upper.iter().map(to_mapping))
    ));
    file
}

fn to_mapping((key, (a, b, c)): (&u32, &(u32, u32, u32))) -> (CharEscape, [CharEscape; 3]) {
    (
        CharEscape(std::char::from_u32(*key).unwrap()),
        [
            CharEscape(std::char::from_u32(*a).unwrap()),
            CharEscape(std::char::from_u32(*b).unwrap()),
            CharEscape(std::char::from_u32(*c).unwrap()),
        ],
    )
}

struct CharEscape(char);

impl fmt::Debug for CharEscape {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "'{}'", self.0.escape_default())
    }
}

static HEADER: &str = "
pub fn to_lower(c: char) -> [char; 3] {
    match bsearch_case_table(c, LOWERCASE_TABLE) {
        None => [c, '\\0', '\\0'],
        Some(index) => LOWERCASE_TABLE[index].1,
    }
}

pub fn to_upper(c: char) -> [char; 3] {
    match bsearch_case_table(c, UPPERCASE_TABLE) {
        None => [c, '\\0', '\\0'],
        Some(index) => UPPERCASE_TABLE[index].1,
    }
}

fn bsearch_case_table(c: char, table: &[(char, [char; 3])]) -> Option<usize> {
    table.binary_search_by(|&(key, _)| key.cmp(&c)).ok()
}
";
