//! Renderer metadata carried by native renderer registrations.

use crate::Error;

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct RendererNameAttribute(String);

impl RendererNameAttribute {
    pub fn new(name: String) -> Result<Self, Error> {
        if name.trim().is_empty() {
            return Err(Error::Argument);
        }
        Ok(Self(name))
    }

    #[must_use]
    pub fn to_string(&self) -> String {
        self.0.clone()
    }
}

impl std::fmt::Display for RendererNameAttribute {
    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        formatter.write_str(&self.0)
    }
}

/// Formats the flags the same way .NET formats a valid `FaceMask` value.
/// Unknown bit combinations remain numeric instead of inventing flag names.
#[must_use]
pub fn face_mask_to_string(mask: crate::rendering::FaceMask) -> String {
    let known = [
        (crate::rendering::FaceMask::SINGLE.0, "Single"),
        (crate::rendering::FaceMask::CAP.0, "Cap"),
        (crate::rendering::FaceMask::END.0, "End"),
        (crate::rendering::FaceMask::SIDE.0, "Side"),
        (crate::rendering::FaceMask::INNER.0, "Inner"),
        (crate::rendering::FaceMask::OUTER.0, "Outer"),
        (crate::rendering::FaceMask::HOLLOW.0, "Hollow"),
        (crate::rendering::FaceMask::OPEN.0, "Open"),
        (crate::rendering::FaceMask::FLAT.0, "Flat"),
        (crate::rendering::FaceMask::TOP.0, "Top"),
        (crate::rendering::FaceMask::BOTTOM.0, "Bottom"),
    ];
    if mask.0 == 0 {
        return "0".to_owned();
    }
    let all_known = known.iter().fold(0, |bits, (flag, _)| bits | flag);
    if mask.0 & !all_known != 0 {
        return mask.0.to_string();
    }
    known
        .into_iter()
        .filter_map(|(flag, name)| (mask.0 & flag != 0).then_some(name))
        .collect::<Vec<_>>()
        .join(", ")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn renderer_name_rejects_empty_metadata() {
        assert!(RendererNameAttribute::new(String::new()).is_err());
        assert_eq!(
            RendererNameAttribute::new("Simple".to_owned())
                .unwrap()
                .to_string(),
            "Simple"
        );
    }

    #[test]
    fn face_mask_uses_dotnet_style_flag_names() {
        let mask = crate::rendering::FaceMask(
            crate::rendering::FaceMask::CAP.0 | crate::rendering::FaceMask::OUTER.0,
        );
        assert_eq!(face_mask_to_string(mask), "Cap, Outer");
        assert_eq!(face_mask_to_string(crate::rendering::FaceMask(0)), "0");
        assert_eq!(
            face_mask_to_string(crate::rendering::FaceMask(1 << 20)),
            "1048576"
        );
    }
}
