extern crate um;
use um::cpu::{Cpu,Console};
use std::io;
use std::io::prelude::*;
use std::fs::File;
use std::vec::Vec; 
use clap::Parser;

/// Run a um program
#[derive(Parser)]
struct Cli {
  // debug level
  #[clap(short, long, default_value_t = 0)]
  debug: u32,
    /// The path to the program to load
    program: std::path::PathBuf
    
}

struct IoConsole {
  inptr: usize,
  inbuf: String
}

impl IoConsole {
  pub fn make() -> IoConsole {
    IoConsole {
      inptr: 0,
      inbuf: String::from("")
    }
  }
}

impl Console for IoConsole {
  fn read(&mut self) -> Option<u8> {
    if self.inptr >= self.inbuf.len() {
      if let Err(_) = std::io::stdin().read_line(&mut self.inbuf) {
        return None;
      }
    }
    let r = self.inbuf.bytes().nth(self.inptr).unwrap();
    self.inptr +=1;
    Some(r)
  }
  fn write(&mut self, c: u8) {
    let buf = [c;1];
    let _ = std::io::stdout().write(&buf);
    let _ = std::io::stdout().flush();
  }
}


fn main() -> io::Result<()> {
  let args = Cli::parse();
  
  let mut pgm = Vec::new(); 
  
  let mut f = File::open(args.program)?;
  let mut cont = true;
  while cont {
    let mut buffer = [0u8; 4];
    cont = match f.read(&mut buffer) {
        Ok(4) => {
          let num = u32::from_be_bytes(
        buffer.try_into().unwrap());
          pgm.push(num);
          true
        },
        Ok(_) => false, 
        Err(_) => false,
    };
  }
  
  let mut c = Cpu::new(pgm);
  c.set_debug(args.debug);
  let mut cons = IoConsole::make();
  match c.run(&mut cons) {
    Err(e) => println!("Error {:?}", e),
    Ok(_) => ()
  }
  Ok(())
}