use std::fs::DirEntry;
use std::io;
use std::path::PathBuf;

fn home_dir() -> PathBuf {
    dirs::home_dir().expect("Couldn't find home dir")
}

fn downloads_dir() -> PathBuf {
    dirs::download_dir().expect("Couldn't find downloads dir")
}

fn pictures_dir() -> PathBuf {
    dirs::picture_dir().expect("Couldn't find pictures dir")
}

fn strip_null_bytes(str: &str) -> String {
    str.replace('\0', "")
}

/// e.g. for ~/Music/ABBA/ABBA Gold:
/// `create_dir_if_not_exists(vec!["Music", "ABBA", "ABBA Gold"])`
fn create_dir_if_not_exists(path: Vec<&str>) -> PathBuf {
    let mut dir = home_dir();

    for item in path {
        dir.push(item);
    }

    std::fs::create_dir_all(&dir).unwrap_or(());

    dir
}

fn move_file(file: PathBuf, mut destination: PathBuf) {
    let file_name = file.file_name().expect("No file name").to_str().unwrap();

    println!("Moving {:?} to {:?}", file_name, destination);

    destination.push(file_name);

    match fs_extra::file::move_file(file, destination, &Default::default()) {
        Ok(_)          => println!("OK!"),
        Err(e) => println!("Couldn't move file: {:?}", e),
    }
}

fn yes() -> bool {
    let mut answer = String::new();

    io::stdin().read_line(&mut answer)
        .expect("Failed to read input");

    answer.to_lowercase().starts_with("y")
}

fn handle_image(file: DirEntry) {
    move_file(file.path(), pictures_dir());
}

fn handle_gif(file: DirEntry) {
    let mut dir = pictures_dir();

    dir.push("Internet");
    dir.push("GIFs");

    move_file(file.path(), dir);
}

fn handle_mp3(file: DirEntry) {
    let meta = mp3_metadata::read_from_file(file.path());

    if meta.is_err() {
        println!("Couldn't read ID3 metadata for file {:?}", file.path());
    }

    if let Some(tag) = meta.unwrap().tag {
        println!("----------------------");
        println!("artist: {}", tag.artist.trim());
        println!("album: {}", tag.album.trim());
        println!("title: {}", tag.title.trim());

        println!("move to: ~/Music/{}/{}/?", tag.artist.trim(), tag.album.trim());

        if yes() {
            move_file(
                file.path(),
                create_dir_if_not_exists(
                    vec![
                        "Music",
                        strip_null_bytes(&tag.artist).trim(),
                        strip_null_bytes(&tag.album).trim()
                    ]
                )
            );
        } else {
            println!("skipping...");
        }
    }
}

fn handle_video(file: DirEntry) {
    // todo if filename contains something like "(1971)" or "[2003]" it's a movie
    // todo if filename contains something like "S03E21" it's a TV programme
    move_file(file.path(), create_dir_if_not_exists(vec!["Videos"]));
}

fn handle_dir(path: PathBuf) {
    let dir = std::fs::read_dir(path).expect("Couldn't read dir");

    for inode in dir {
        let inode = inode.expect("Couldn't read inode");

        // recursively handle directories
        if inode.metadata().unwrap().is_dir() {
            handle_dir(inode.path());
        }

        if let Some(extension) = inode.path().extension().unwrap_or("none".as_ref()).to_str() {
            match extension.to_string().to_lowercase().as_ref() {
                "gif"  => { handle_gif(  inode) }
                "jpg"  => { handle_image(inode) }
                "jpeg" => { handle_image(inode) }
                "png"  => { handle_image(inode) }
                "mp3"  => { handle_mp3(  inode) }
                "mp4"  => { handle_video(  inode) }
                // todo m4a, flac, etc?
                _ => { /*println!("Here's where we would do nothing.");*/ }
            }
        }
    }
}

fn main() {
    handle_dir(downloads_dir());
}