use std::io;

use io_uring::IoUring;

use crate::driver::{Driver, FixedBufGuard, ProvidedBuf};
use crate::sys::Cpu;
use dope_core::driver as caps;

pub fn new_driver(cfg: crate::DriverConfig) -> io::Result<Driver> {
    tracing::info!("dope: io-uring driver");
    if let Some(cpu) = cfg.cpu_id {
        Cpu::pin(cpu)?;
    }
    let uring = open_uring(&cfg)?;
    if !uring.params().is_feature_ext_arg() {
        return Err(io::Error::new(
            io::ErrorKind::Unsupported,
            "dope: io_uring IORING_FEAT_EXT_ARG is required",
        ));
    }

    let ring = crate::driver::Ring::new(uring);
    let hot_cap = (cfg.ring_entries as usize).saturating_mul(8).max(4096);
    Driver::new(ring, cfg, hot_cap)
}

fn open_uring(cfg: &crate::DriverConfig) -> io::Result<IoUring> {
    let mut builder = IoUring::builder();
    builder.setup_single_issuer();
    builder.setup_submit_all();
    if cfg.defer_taskrun {
        builder.setup_defer_taskrun();
    }
    builder.build(cfg.ring_entries)
}

impl caps::DriverLifecycle for Driver {
    #[inline(always)]
    fn drive(&mut self) -> io::Result<bool> {
        Self::drive(self)
    }
    #[inline(always)]
    fn has_pending(&mut self) -> bool {
        Self::has_pending(self)
    }
    #[inline(always)]
    fn park(&mut self) -> io::Result<()> {
        Self::park(self)
    }
    #[inline(always)]
    fn park_for(&mut self, duration: std::time::Duration) -> io::Result<()> {
        Self::park_for(self, duration)
    }
    #[inline(always)]
    fn submit_only(&mut self) -> io::Result<()> {
        Self::submit_only(self)
    }
}

impl caps::FixedBuffers for Driver {
    type Guard = FixedBufGuard;

    #[inline(always)]
    fn fixed_guard(&mut self) -> Option<Self::Guard> {
        self.resources.fixed_guard()
    }

    #[inline(always)]
    fn register_fixed(&mut self, fd: crate::Fd) -> io::Result<Option<u32>> {
        self.resources.register_fixed(&self.ring, fd)
    }

    #[inline(always)]
    fn unregister_fixed(&mut self, idx: u32) {
        self.resources.unregister_fixed(&self.ring, idx);
    }
}

impl caps::ProvidedBufRing for Driver {
    type Provided = ProvidedBuf;

    #[inline(always)]
    fn provided_group(&mut self) -> Option<(u16, u32)> {
        Some(self.provided.group())
    }

    #[inline(always)]
    fn provided(&mut self, bid: u16, len: usize) -> Option<Self::Provided> {
        Some(self.provided.make_buf(bid, len))
    }
}
