use std::{io, env, fs, process};

/*
Creates something readble to get the data; possible values returned from this
function are:

    - an opened file
    - the standard input

Which one is determined by whether a file was passed as an argument to the
program or not.
*/
pub fn create_reader() -> Box<io::Read> {
    // If something is provided as an argument to this program, expect it to be
    // the name of the file containing the data.
    //
    // This attempts to extract the filename from the command-line arguments.
    let cli_filename = env::args().nth(1);

    // Create an io::Read from the extracted filename.
    //
    // If nothing was provided (meaning nth returned a None above), use the
    // stdin to get the data.
    let input: Box<io::Read> = match cli_filename {
        None => {
            // As stated before, if no filename is provided, use stdin to read
            // the data; it stops reading once an EOF (End Of File, Ctrl+d for
            // Linux, Ctrl+z for Windows) is found.
            Box::new(io::stdin())
        },
        Some(filename) => {
            // Some filename was provided, so now we need to open it. But we
            // still have no guarantees that the file actually exists in the
            // system, so some checks are needed.
            let file = match fs::File::open(filename.clone()) {
                Ok(f) => {
                    // No problems, file found and opened. Return it.
                    f
                },
                Err(e) => {
                    // If we're here, the file was not found in the system or
                    // some other error occurred. Report it and exit the
                    // program with the error code 1.
                    println!("Error opening file {}:\n\t{}", filename, e);
                    process::exit(1);
                },
            };

            // We get here once we've successfully opened the file; this means
            // our job is done. Grab a cofee and pass the work to the next
            // statement.
            Box::new(file)
        },
    };

    input
}

/*
Read the data from an io::Read and return it as a String.
*/
pub fn read_data(reader: &mut io::Read) -> String {
    // Place to store the data read from the reader.
    let mut buffer = String::new();

    if let Err(e) = reader.read_to_string(&mut buffer) {
        // Some error occurred while reading the data, report and exit with
        // error code 2.
        println!("Error reading data from file:\n\t{}", e);
        process::exit(2);
    };

    // If we get here, our job is done. Return the read data.
    buffer
}
