#![cfg(target_os = "linux")]

pub mod driver;
pub mod sys;

pub(crate) use dope_core::{Fd, FixedFd};

pub use driver::config::DriverConfig;
pub use driver::handle::new_driver;
pub use driver::{
    CqeKind, CqeRouter, CqeView, CurrentSlot, Driver, ProvidedRing, RecvMsgOut, Request, current,
    current_driver_mut, set_current,
};

#[derive(Clone, Copy, Debug, Default)]
pub struct UringBackend;

impl dope_core::driver::Backend for UringBackend {
    type Driver = driver::Driver;
    type Config = DriverConfig;
    type RecvMsgOut = driver::RecvMsgOut;
    type ProvidedBuf = driver::ProvidedBuf;

    fn new_driver(cfg: Self::Config) -> std::io::Result<Self::Driver> {
        new_driver(cfg)
    }
}

struct CompletionShim<'a, R: dope_core::driver::CompletionRouter<UringBackend>> {
    inner: &'a mut R,
}

impl<R: dope_core::driver::CompletionRouter<UringBackend>> CqeRouter for CompletionShim<'_, R> {
    #[inline(always)]
    fn on_cqe(&mut self, driver: &mut Driver, cqe: CqeView) {
        use dope_core::driver::Completion;
        let ev = match cqe.kind() {
            CqeKind::Accept(t) => Completion::Accept {
                token: t,
                result: cqe.result,
                more: cqe.more(),
            },
            CqeKind::Recv(t) => Completion::Recv {
                token: t,
                result: cqe.result,
                more: cqe.more(),
                bid: cqe.buffer_select(),
            },
            CqeKind::Write(t) => Completion::Write {
                token: t,
                result: cqe.result,
                notif: cqe.notif(),
            },
        };
        self.inner.on_complete(driver, ev);
    }
}

thread_local! {
    static AUX: std::cell::Cell<Option<dope_core::driver::ExternalDispatch<UringBackend>>> =
        const { std::cell::Cell::new(None) };
}

impl dope_core::driver::CompletionBackend for UringBackend {
    type Token = sys::token::Token;

    #[inline(always)]
    fn current_slot() -> Option<dope_core::driver::CurrentSlot<Self>> {
        driver::current()
    }

    #[inline(always)]
    fn set_slot(slot: Option<dope_core::driver::CurrentSlot<Self>>) {
        driver::set_current(slot);
    }

    #[inline(always)]
    fn install_external(
        aux: Option<dope_core::driver::ExternalDispatch<Self>>,
    ) -> Option<dope_core::driver::ExternalDispatch<Self>> {
        AUX.with(|c| c.replace(aux))
    }

    #[inline(always)]
    fn current_external() -> Option<dope_core::driver::ExternalDispatch<Self>> {
        AUX.with(|c| c.get())
    }

    #[inline(always)]
    fn drive_with<R: dope_core::driver::CompletionRouter<Self>>(
        driver: &mut Self::Driver,
        router: &mut R,
    ) -> std::io::Result<bool> {
        let mut shim = CompletionShim { inner: router };
        driver.drive_with(&mut shim)
    }

    #[inline(always)]
    fn dispatch_completions<R: dope_core::driver::CompletionRouter<Self>>(
        driver: &mut Self::Driver,
        router: &mut R,
    ) {
        let mut shim = CompletionShim { inner: router };
        driver.dispatch_cqes(&mut shim)
    }
}

pub(crate) use sys::Sqe;
pub use sys::token::Token;
