
pub fn convert_byte_array_to_string(data: &[u8]) -> String {
    if data.is_empty() {
        return String::new();
    }

    let end = data.iter().position(|&b| b == 0).unwrap_or(data.len());
    let valid = &data[..end];
    if valid.is_empty() {
        return String::new();
    }

    decode_ethercat_string(valid)
}

pub fn decode_ethercat_string(data: &[u8]) -> String {
    if data.is_empty() {
        return String::new();
    }

    if let Ok(s) = std::str::from_utf8(data) {
        if has_meaningful_content(s) {
            return clean_string(s);
        }
    }

    if data.iter().all(|&b| b < 128) {

        let s = std::str::from_utf8(data).unwrap_or("");
        if has_meaningful_content(s) {
            return clean_string(s);
        }
    }

    let latin1: String = data.iter().map(|&b| b as char).collect();
    clean_string(&latin1)
}

fn has_meaningful_content(s: &str) -> bool {
    if s.is_empty() {
        return false;
    }
    s.chars().any(|c| !c.is_control() || c == ' ')
}

fn clean_string(s: &str) -> String {

    let trimmed_end = s.trim_end_matches(|c: char| c == '\0' || c == '\r' || c == '\n' || c == '\t');

    let result = trimmed_end.trim_start_matches(|c: char| c.is_control() && c != ' ');
    result.to_string()
}

pub fn convert_fixed_name_to_string(bytes: &[u8; 41]) -> String {
    convert_byte_array_to_string(bytes)
}

pub fn bytes_to_hex_display(data: &[u8]) -> String {
    data.iter()
        .map(|b| format!("{:02X}", b))
        .collect::<Vec<_>>()
        .join(" ")
}

pub fn ip_to_string(ip: &[u8; 4]) -> String {
    format!("{}.{}.{}.{}", ip[0], ip[1], ip[2], ip[3])
}
