// MMIO bus: dispatches reads/writes to registered devices by GPA range.
// Status: minimal — Vec<(range, dev)> linear scan. Fast enough at
// the device counts we run (<10).

use std::sync::{Arc, Mutex};

pub trait MmioDevice: Send + Sync {
    /// Read `size` bytes (1, 2, 4, or 8) from `offset` within the
    /// device's MMIO range. Returns the value zero-extended to u64.
    fn read(&self, offset: u64, size: u8) -> u64;
    /// Write `size` bytes (low bits of `value`) to `offset`.
    fn write(&self, offset: u64, value: u64, size: u8);
    /// Length of the MMIO region (used to register on the bus).
    fn len(&self) -> u64;
}

pub struct MmioBus {
    /// (base_gpa, len, device).
    entries: Mutex<Vec<(u64, u64, Arc<dyn MmioDevice>)>>,
}

impl MmioBus {
    pub fn new() -> Self {
        Self {
            entries: Mutex::new(Vec::new()),
        }
    }

    pub fn register(&self, base: u64, dev: Arc<dyn MmioDevice>) {
        let len = dev.len();
        self.entries.lock().unwrap().push((base, len, dev));
    }

    /// Returns Some((device, offset)) if `gpa` falls within a
    /// registered region, else None.
    pub fn lookup(&self, gpa: u64) -> Option<(Arc<dyn MmioDevice>, u64)> {
        let g = self.entries.lock().unwrap();
        for (base, len, dev) in g.iter() {
            if gpa >= *base && gpa < base + len {
                return Some((dev.clone(), gpa - base));
            }
        }
        None
    }

    pub fn read(&self, gpa: u64, size: u8) -> Option<u64> {
        let (dev, off) = self.lookup(gpa)?;
        Some(dev.read(off, size))
    }

    pub fn write(&self, gpa: u64, value: u64, size: u8) -> bool {
        if let Some((dev, off)) = self.lookup(gpa) {
            dev.write(off, value, size);
            true
        } else {
            false
        }
    }
}
