use clap::Parser;
use tool_helper::{Error, exit_with_error};
use readmap::types::MemoryMap;
use std::{io::{BufRead, BufReader}, path::PathBuf, process::{Child, Command, Stdio}};

pub const RUN_DUMP_TOOL_IN_WSL:bool = cfg!(windows);

#[derive(Parser)]
#[clap(about = "Opens and scans an ELF file to print extended information", long_about = None)]
struct CommandLine {
    #[clap(value_parser, help="Input ELF file for scannning")]
    input:   PathBuf,
    #[clap(long="wsl", help="Run \"objdump\" in WSL", default_value_t=RUN_DUMP_TOOL_IN_WSL)]
    use_wsl: bool,
    #[clap(short='o', help="Output a memory map file with running the tool")]
    output:  Option<PathBuf>
}

pub fn main() {
    match CommandLine::try_parse() {
        Ok(cmd_line) => {
            match run_main(cmd_line) {
                Ok(_)      => (),
                Err(error) => exit_with_error(error)
            }
        },
        Err(error) => {
            println!("{}", error)
        }
    }
}

fn run_objdump(use_wsl: bool, input: PathBuf) -> Result<Child, Error> {
    let command_list = ["wsl", "objdump", "-x"];
    let start_idx    = {
        if use_wsl {0}
        else       {1}
    };
    let mut process = Command::new(command_list[start_idx]);

    for arg in command_list.iter().skip(start_idx + 1) {
        process.arg(arg);
    }
    process.arg(input.to_str().ok_or_else(||{Error::from_str("Failed converting input string")})?);

    Ok(process.stdout(Stdio::piped()).spawn()?)
}

fn run_main(cmd: CommandLine) -> Result<(), Error> {
    let output     = tool_helper::open_output(cmd.output)?;
    let memory_map = generate_memory_map(cmd.use_wsl, cmd.input)?;

    readmap::dump::write(output, &memory_map)?;
    Ok(())
}

fn generate_memory_map(use_wsl: bool, input: PathBuf) -> Result<MemoryMap, Error> {
    let mut child = run_objdump(use_wsl, input)?;
    if let Some(stdout) = &mut child.stdout {
        let mut line_iter = BufReader::new(stdout).lines().map(|l| l.unwrap()).into_iter();
        
        readmap::scan(|| {
            line_iter.next()
        })
    }

    else {
        Err(Error::from_str("Failed opening \"stdout\" for objdump"))
    }    
}
