// /// String that gets cropped if it exceeds a certain size.
// #[derive(PartialEq, Eq, Hash, Debug)]
// struct SizeCappedString<const N: usize> {
//     inner: String,
//     length: usize,
//     is_cropped: bool,
// }

// impl<const N: usize> SizeCappedString<N> {
//     const MAX_SIZE: usize = N;

//     pub fn new(data: String) -> Self {
//         let length = data.len();

//         if length > Self::MAX_SIZE {
//             Self {
//                 inner: data[..Self::MAX_SIZE].to_string(),
//                 length,
//                 is_cropped: true,
//             }
//         } else {
//             Self {
//                 inner: data,
//                 length,
//                 is_cropped: false,
//             }
//         }
//     }

//     pub fn to_string_lossy(&self) -> String {
//         self.inner.clone()
//     }

//     pub fn to_string_marked(&self, mark: &str) -> String {
//         if self.is_cropped {
//             format!("{}{}", self.inner, mark)
//         } else {
//             self.inner.clone()
//         }
//     }

//     pub fn to_string(&self) -> Option<String> {
//         if self.is_cropped {
//             None
//         } else {
//             Some(self.inner.clone())
//         }
//     }
// }

/// String with a consistent size.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StaticString<const N: usize>([char; N]);

impl<const N: usize> From<&StaticString<N>> for String {
    fn from(value: &StaticString<N>) -> Self {
        let mut result = String::new();
        for &c in value.0.iter() {
            if c == 0 as char {
                break;
            }
            result.push(c);
        }
        result
    }
}

impl<const N: usize> From<String> for StaticString<N> {
    fn from(value: String) -> Self {
        let mut s = Self::new();
        for (i, c) in value.chars().take(N).enumerate() {
            s.0[i] = c;
        }
        s
    }
}

impl<const N: usize> Default for StaticString<N> {
    fn default() -> Self {
        Self::new()
    }
}

impl<const N: usize> StaticString<N> {
    pub fn new() -> Self {
        StaticString([0 as char; N])
    }

    pub fn is_empty(&self) -> bool {
        let first = self.0.first();
        first.is_none() || first == Some(&(0 as char))
    }
}

pub fn insensitive_string(s: &str) -> String {
    let out = s.to_lowercase();

    assert_eq!(out.len(), s.len());
    out
}

pub fn remove_bad_words(s: &str, wordlist: &[String], censor_char: char) -> String {
    let mut s = s.to_string();
    let mut s_ins = insensitive_string(&s);

    for word in wordlist {
        let word_len = word.len();
        let replacement = censor_char.to_string().repeat(word_len);

        while let Some(start) = s_ins.find(word) {
            let end = start + word_len;
            s.replace_range(start..end, &replacement);
            s_ins = insensitive_string(&s);
        }
    }

    s
}
