use std::{
    env,
    io::{self, Read},
    process::ExitCode,
};

use sysutils::{Input, ToExitCode};

const WINDOW_SIZE: usize = 16;

fn do_line(offset: usize, line: &[u8]) {
    print!("{:08x} |", offset);

    for i in 0..WINDOW_SIZE {
        if i % 2 == 0 {
            print!(" ");
        }
        if i < line.len() {
            print!("{:02X}", line[i]);
        } else {
            print!("  ");
        }
    }

    print!(" | ");

    for &ch in line {
        let ch = if ch.is_ascii_control() { b'.' } else { ch };
        print!("{}", ch as char);
    }

    println!();
}

fn do_file(path: &str) -> io::Result<()> {
    let mut input = Input::open_str(path)?;
    let mut buf = [0; WINDOW_SIZE];
    let mut offset = 0;

    loop {
        let len = input.read(&mut buf)?;

        if len == 0 {
            break;
        }

        do_line(offset, &buf[..len]);
        offset += len;
    }

    Ok(())
}

pub fn main() -> ExitCode {
    let args = env::args().collect::<Vec<_>>();

    let result = match args.len() {
        0..=1 => do_file("-").to_exit_code(),
        2 => do_file(&args[1]).to_exit_code(),
        _ => {
            let mut result = 0;
            for arg in args[1..].iter() {
                println!("{}:", arg);
                if do_file(arg).to_exit_code() != 0 {
                    result = 1;
                }
            }
            result
        }
    };

    if result == 0 {
        ExitCode::SUCCESS
    } else {
        ExitCode::FAILURE
    }
}
