use std::marker::PhantomData;

use crate::CodecLayer;
use dope_core::driver::{Completion, CompletionRouter, DriverLifecycle, PoolDriver};
use dope_core::profile::Profile;
use dope_runtime::runtime::Manifold;
use dope_transport::Transport;

pub mod client;
pub(crate) mod core;
pub mod server;
pub mod slot;

use self::core::PoolCore;

pub trait Direction<B: PoolDriver>: 'static {
    type Transport: Transport;
    type SlotUser: Default + 'static;
    type CodecLayer: CodecLayer;

    fn has_pending(&self) -> bool;

    fn tick(
        &mut self,
        core: &mut PoolCore<Self::Transport, Self::CodecLayer, Self::SlotUser, B>,
        driver: &mut B::Driver,
    );

    #[inline(always)]
    fn on_accept_event(&mut self, _token: B::Token, _result: i32, _more: bool) {}
}

pub struct Pool<D: Direction<B>, F: Profile, B: PoolDriver> {
    pub(crate) direction: D,
    pub(crate) core: PoolCore<D::Transport, D::CodecLayer, D::SlotUser, B>,
    pub(crate) profile: PhantomData<F>,
}

pub trait PoolTick<B: PoolDriver> {
    fn tick(&mut self, driver: &mut B::Driver);
}

pub(crate) trait PoolRecv<B: PoolDriver> {
    fn on_recv_event(
        &mut self,
        driver: &mut B::Driver,
        token: B::Token,
        result: i32,
        more: bool,
        bid: Option<u16>,
    );
}

pub(crate) trait PoolWrite<B: PoolDriver> {
    fn on_write_event(&mut self, driver: &mut B::Driver, token: B::Token, result: i32, notif: bool);
}

impl<D, F, B> Manifold for Pool<D, F, B>
where
    D: Direction<B>,
    F: Profile,
    B: PoolDriver,
    Self: PoolTick<B> + CompletionRouter<B>,
{
    type Backend = B;

    #[inline(always)]
    fn has_pending(&self, _driver: &mut B::Driver) -> bool {
        F::HYBRID_PARK && self.direction.has_pending()
    }

    #[inline(always)]
    fn drive(&mut self, driver: &mut B::Driver) {
        B::dispatch_completions(driver, self);
        <Self as PoolTick<B>>::tick(self, driver);
        let _ = driver.submit_only();
    }
}

impl<D, F, B> CompletionRouter<B> for Pool<D, F, B>
where
    D: Direction<B>,
    F: Profile,
    B: PoolDriver,
    Self: PoolRecv<B> + PoolWrite<B>,
{
    #[inline(always)]
    fn on_complete(&mut self, driver: &mut B::Driver, ev: Completion<B>) {
        match ev {
            Completion::Recv {
                token,
                result,
                more,
                bid,
            } => self.on_recv_event(driver, token, result, more, bid),
            Completion::Write {
                token,
                result,
                notif,
            } => self.on_write_event(driver, token, result, notif),
            Completion::Accept {
                token,
                result,
                more,
            } => self.direction.on_accept_event(token, result, more),
        }
    }
}
