use crate::memory::{ActiveInterrupt, Memory};
use crate::roms::RomFile;
pub struct BlockMemory {
  size: usize,
  data: Vec<u8>,
  persistent: bool,
  writeable: bool,
}
impl BlockMemory {
  pub fn ram(size: usize) -> Self {
    Self {
      size,
      data: vec![0; size],
      persistent: false,
      writeable: true,
    }
  }
  pub fn rom(size: usize) -> Self {
    Self {
      size,
      data: vec![0; size],
      persistent: true,
      writeable: false,
    }
  }
  pub fn from_file(size: usize, file: RomFile) -> Self {
    let mut data = vec![0; size];
    let file_data = file.get_data();
    if file_data.len() > size {
      panic!(
        "File of size {} is too large for memory block of size {}",
        file_data.len(),
        size
      );
    }
    data[..file_data.len()].copy_from_slice(&file_data[..]);
    Self {
      size,
      data,
      persistent: true,
      writeable: false,
    }
  }
  pub fn set_writeable(mut self, writeable: bool) -> Self {
    self.writeable = writeable;
    self
  }
}
impl Memory for BlockMemory {
  fn read(&mut self, address: u16) -> u8 {
    self.data[(address as usize) % self.size]
  }
  fn write(&mut self, address: u16, value: u8) {
    if self.writeable {
      self.data[(address as usize) % self.size] = value;
    }
  }
  fn reset(&mut self) {
    if !self.persistent {
      for i in 0..self.data.len() {
        self.data[i] = 0;
      }
    }
  }
  fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> ActiveInterrupt {
    ActiveInterrupt::None
  }
}
#[cfg(test)]
mod tests {
  use super::*;
  #[test]
  fn test_ram() {
    let mut mem = BlockMemory::ram(0x1000);
    assert_eq!(0x00, mem.read(0x123));
    mem.write(0x123, 0x45);
    assert_eq!(0x45, mem.read(0x123));
    assert_eq!(0x00, mem.read(0x124));
    assert_eq!(0x45, mem.read(0x1123));
    mem.reset();
    assert_eq!(0x00, mem.read(0x123));
  }
  #[test]
  fn test_rom() {
    let mut mem = BlockMemory::rom(0x1000);
    assert_eq!(0x00, mem.read(0x123));
    mem.write(0x123, 0x45);
    assert_eq!(0x00, mem.read(0x123));
    mem.reset();
  }
  #[test]
  fn test_from_file() {
    let file = RomFile::new(vec![0x12, 0x34, 0x56, 0x78]);
    let mut mem = BlockMemory::from_file(0x1000, file);
    assert_eq!(0x12, mem.read(0x000));
    assert_eq!(0x34, mem.read(0x001));
    assert_eq!(0x56, mem.read(0x002));
    assert_eq!(0x78, mem.read(0x003));
    assert_eq!(0x12, mem.read(0x1000));
    mem.reset();
    assert_eq!(0x12, mem.read(0x000));
    assert_eq!(0x00, mem.read(0x004));
    assert_eq!(0x00, mem.read(0xFFF));
  }
  #[test]
  #[should_panic]
  fn test_from_file_too_large() {
    let file = RomFile::new(vec![0x12, 0x34, 0x56, 0x78]);
    BlockMemory::from_file(0x03, file);
  }
}