//! Value conversion helpers used by the generated `COLLADA 1.4` data model.

fn regex_whitespace_split(value: &str) -> Vec<&str> {
    let mut values = Vec::new();
    let mut start = 0;
    let mut whitespace = false;
    for (index, character) in value.char_indices() {
        if character.is_whitespace() {
            if !whitespace {
                values.push(&value[start..index]);
                whitespace = true;
            }
        } else if whitespace {
            start = index;
            whitespace = false;
        }
    }
    if whitespace {
        values.push("");
    } else {
        values.push(&value[start..]);
    }
    values
}

pub(crate) fn parse_bools(value: &str) -> Vec<bool> {
    regex_whitespace_split(value)
        .into_iter()
        .map(|value| value == "true")
        .collect()
}

pub(crate) fn parse_doubles(value: &str) -> Vec<f64> {
    regex_whitespace_split(value)
        .into_iter()
        .map(|value| value.parse().unwrap_or_default())
        .collect()
}

pub(crate) fn parse_longs(value: &str) -> Vec<i64> {
    regex_whitespace_split(value)
        .into_iter()
        .map(|value| value.parse().unwrap_or_default())
        .collect()
}

pub(crate) fn parse_names(value: &str) -> Vec<String> {
    regex_whitespace_split(value)
        .into_iter()
        .map(str::to_owned)
        .collect()
}

pub(crate) fn format_bools(values: &[bool]) -> String {
    values
        .iter()
        .map(|value| if *value { "true" } else { "false" })
        .collect::<Vec<_>>()
        .join(" ")
}

pub(crate) fn format_values<T: ToString>(values: &[T]) -> String {
    values
        .iter()
        .map(ToString::to_string)
        .collect::<Vec<_>>()
        .join(" ")
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::import_export::collada14::{AssetUnit, Node, NodeType};

    #[test]
    fn whitespace_split_matches_regex_split_boundaries() {
        assert_eq!(parse_names(""), vec![""]);
        assert_eq!(parse_names(" a\t b "), vec!["", "a", "b", ""]);
    }

    #[test]
    fn invalid_numeric_elements_follow_try_parse_defaults() {
        assert_eq!(parse_doubles("1 nope 2.5"), vec![1.0, 0.0, 2.5]);
        assert_eq!(parse_longs("1 nope -2"), vec![1, 0, -2]);
    }

    #[test]
    fn generated_schema_properties_round_trip_owned_values() {
        let mut unit = AssetUnit::new().unwrap();
        assert_eq!(unit.meter(), 1.0);
        assert_eq!(unit.name(), "meter");
        unit.set_meter(0.3048);
        unit.set_name("foot".into());
        assert_eq!(unit.meter(), 0.3048);
        assert_eq!(unit.name(), "foot");
    }

    #[test]
    fn generated_schema_constructors_preserve_pinned_defaults() {
        let node = Node::new().unwrap();
        assert_eq!(node.type_(), NodeType::NODE);
        assert!(node.items().is_empty());
    }
}
