use std::path::{Component, Path, PathBuf};

pub fn child_to_abs_path<P: AsRef<Path>, C: AsRef<Path>>(parent: P, child: C) -> PathBuf {
    let parent_path = parent.as_ref();
    let child_path = child.as_ref();

    if child_path.is_absolute() {
        return child_path.to_path_buf();
    }

    let parent_path_buf = PathBuf::from(parent_path);
    clean_path(parent_path_buf.join(child_path))
}

pub fn child_to_abs_path_string<P: AsRef<Path>, C: AsRef<Path>>(parent: P, child: C) -> String {
    child_to_abs_path(parent, child)
        .to_string_lossy()
        .to_string()
}

pub fn child_to_relative_path<P: AsRef<Path>, C: AsRef<Path>>(parent: P, child: C) -> PathBuf {
    child
        .as_ref()
        .strip_prefix(parent.as_ref())
        .unwrap_or_else(|_| child.as_ref())
        .to_owned()
}

pub fn child_to_relative_path_string<P: AsRef<Path>, C: AsRef<Path>>(
    parent: P,
    child: C,
) -> String {
    child_to_relative_path(parent, child)
        .to_string_lossy()
        .to_string()
}

pub fn clean_path<P: AsRef<Path>>(path: P) -> PathBuf {
    let path = path.as_ref();
    let mut out = PathBuf::new();

    for (i, component) in path.components().enumerate() {
        match component {
            Component::CurDir if i > 0 => {}
            other => out.push(other),
        }
    }

    out
}

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

    #[test]
    fn test_clean_path() {
        assert_eq!(clean_path("./test/./test"), PathBuf::from("./test/test"));
        assert_eq!(clean_path("./test/././test"), PathBuf::from("./test/test"));
        assert_eq!(clean_path("/test/test"), PathBuf::from("/test/test"));
    }

    #[test]
    fn to_absolute_path() {
        assert_eq!(
            child_to_abs_path("/parent", "child"),
            PathBuf::from("/parent/child")
        );
        assert_eq!(
            child_to_abs_path("/parent", "./child"),
            PathBuf::from("/parent/child")
        );
        assert_eq!(
            child_to_abs_path("/parent", "././child"),
            PathBuf::from("/parent/child")
        );
        assert_eq!(child_to_abs_path("/", "././child"), PathBuf::from("/child"));
    }

    #[test]
    fn to_relative_path() {
        assert_eq!(
            child_to_relative_path("/parent", "/parent/child"),
            PathBuf::from("child")
        );
        assert_eq!(
            child_to_relative_path("/bleh", "/parent/child"),
            PathBuf::from("/parent/child")
        );
    }
}
