use core::mem::size_of;

use libk::dma::BusAddress;
use libk_mm::device::DeviceMemoryIo;
use tock_registers::{
    interfaces::{Readable, Writeable},
    registers::WriteOnly,
};

use crate::{
    queue::{VirtQueue, VqNotificationMechanism},
    CommonConfiguration, DeviceStatus,
};

#[cfg(any(feature = "pci", rust_analyzer))]
pub mod pci;

pub trait Transport: Send {
    fn common_cfg(&self) -> &CommonConfiguration;
    fn notify_cfg(&self) -> &[WriteOnly<u16>];
    fn notify_off_mul(&self) -> usize;
    fn supports_msix(&self) -> bool;
    fn device_cfg(&self) -> Option<&DeviceMemoryIo<'_, [u8]>>;
    fn read_interrupt_status(&self) -> (bool, bool);

    fn read_device_features(&mut self) -> u64 {
        let cfg = self.common_cfg();
        cfg.device_feature_select.set(0);
        let low = cfg.device_feature.get();
        cfg.device_feature_select.set(1);
        let high = cfg.device_feature.get();

        (low as u64) | ((high as u64) << 32)
    }

    fn write_driver_features(&mut self, value: u64) {
        let cfg = self.common_cfg();
        cfg.driver_feature_select.set(0);
        cfg.driver_feature.set(value as u32);
        cfg.driver_feature_select.set(1);
        cfg.driver_feature.set((value >> 32) as u32);
    }

    fn read_device_status(&mut self) -> DeviceStatus {
        let cfg = self.common_cfg();
        DeviceStatus::from_bits_retain(cfg.device_status.get())
    }

    fn write_device_status(&mut self, value: DeviceStatus) {
        let cfg = self.common_cfg();
        cfg.device_status.set(value.bits());
    }

    fn max_queue_size(&mut self, queue: u16) -> u32 {
        let cfg = self.common_cfg();
        cfg.queue_select.set(queue);
        cfg.queue_size.get().into()
    }

    fn set_queue<N: VqNotificationMechanism>(
        &mut self,
        index: u16,
        queue: &VirtQueue<N>,
        msix_vector: Option<u16>,
    ) {
        self.set_queue_raw(
            index,
            queue.capacity(),
            queue.descriptor_table_base(),
            queue.available_ring_base(),
            queue.used_ring_base(),
            msix_vector,
        );
    }

    fn set_queue_raw(
        &mut self,
        queue: u16,
        capacity: u16,
        descriptor_table_phys: BusAddress,
        available_ring_phys: BusAddress,
        used_ring_phys: BusAddress,
        msix_vector: Option<u16>,
    ) {
        let cfg = self.common_cfg();
        cfg.queue_select.set(queue);
        cfg.queue_size.set(capacity);
        cfg.queue_desc.set(descriptor_table_phys.into_u64());
        cfg.queue_driver.set(available_ring_phys.into_u64());
        cfg.queue_device.set(used_ring_phys.into_u64());
        if self.supports_msix() {
            cfg.queue_msix_vector.set(msix_vector.unwrap_or(0xFFFF));
        } else {
            cfg.queue_msix_vector.set(0xFFFF);
        }
        cfg.queue_enable.set(1);
    }

    fn unset_queue(&mut self, _queue: u16) {
        todo!()
    }

    fn notify(&mut self, queue: u16) {
        let cfg = self.common_cfg();
        let notify = self.notify_cfg();

        cfg.queue_select.set(queue);
        let notify_off = cfg.queue_notify_off.get() as usize;
        let index = (notify_off * self.notify_off_mul()) / size_of::<u16>();

        notify[index].set(queue);
    }
}
