use common::prelude::Instruction;
use rustc_hash::FxHashMap;

#[derive(Debug)]
pub struct Cache {
    addr: u32,
    instruction_block: Option<[u8; 256]>,
    instruction_lookup: FxHashMap<u32, Instruction>,
}

impl Cache {
    #[must_use]
    pub fn new() -> Self {
        Self {
            addr: 0,
            instruction_block: None,
            instruction_lookup: FxHashMap::default(),
        }
    }

    pub fn lookup_value(&mut self, addr: u32) -> Option<u32> {
        if addr < self.addr || addr >= self.addr + 256 || self.instruction_block.is_none()
        {
            return None;
        }

        Some(u32::from_be_bytes(
            self.instruction_block.expect("this should not be none!")
                [(addr - self.addr) as usize..(addr - self.addr + 4) as usize]
                .try_into()
                .expect("Failed to convert bytes to u32"),
        ))
    }

    pub const fn set(&mut self, addr: u32, block: &[u8; 256]) {
        self.addr = addr - addr % 256;
        self.instruction_block = Some(*block);
    }

    pub fn lookup_instruction(&mut self, instruction: u32) -> Option<Instruction> {
        self.instruction_lookup.get(&instruction).copied()
    }

    pub fn insert(&mut self, value: u32, instruction: Instruction) {
        self.instruction_lookup.insert(value, instruction);
    }
}

impl Default for Cache {
    fn default() -> Self {
        Self::new()
    }
}
