use nom::{
    IResult, Parser,
    branch::alt,
    bytes::complete::tag,
    character::complete::{alpha1, char, digit1, hex_digit1, one_of},
    combinator::{map, map_opt, recognize},
    sequence::{delimited, preceded},
};

use crate::parser::util::ENTITIES;

pub(super) fn html_entity() -> impl FnMut(&str) -> IResult<&str, String> {
    move |input: &str| alt((html_entity_alpha(), html_entity_numeric)).parse(input)
}

fn html_entity_alpha() -> impl FnMut(&str) -> IResult<&str, String> {
    move |input: &str| {
        map_opt(recognize((char('&'), alpha1, char(';'))), |s: &str| {
            ENTITIES.get(s).map(|entity| entity.characters.to_owned())
        })
        .parse(input)
    }
}

fn html_entity_numeric(input: &str) -> IResult<&str, String> {
    let base16 = map_opt(preceded(one_of("xX"), hex_digit1), |s: &str| {
        u32::from_str_radix(s, 16).ok()
    });
    let base10 = map_opt(digit1, |s: &str| s.parse::<u32>().ok());

    map(
        map_opt(
            delimited(tag("&#"), alt((base10, base16)), char(';')),
            char::from_u32,
        ),
        |c: char| c.to_string(),
    )
    .parse(input)
}
