Documentation
use autd3_core::{
    ethercat::DcSysTime,
    geometry::Geometry,
    link::{Link, LinkError, RxMessage, TxBufferPoolSync, TxMessage},
};

use autd3_firmware_emulator::CPUEmulator;

/// A [`Link`] that does nothing.
///
/// This link is mainly used for explanation.
#[derive(Default)]
pub struct Nop {
    cpus: Vec<CPUEmulator>,
    buffer_pool: TxBufferPoolSync,
}

impl Nop {
    /// Creates a new [`Nop`].
    pub const fn new() -> Self {
        Self {
            cpus: Vec::new(),
            buffer_pool: TxBufferPoolSync::new(),
        }
    }
}

impl Link for Nop {
    fn open(&mut self, geometry: &Geometry) -> Result<(), LinkError> {
        self.cpus = geometry
            .iter()
            .enumerate()
            .map(|(i, dev)| CPUEmulator::new(i, dev.num_transducers()))
            .collect();
        self.buffer_pool.init(geometry);
        Ok(())
    }

    fn close(&mut self) -> Result<(), LinkError> {
        self.cpus.clear();
        Ok(())
    }

    fn alloc_tx_buffer(&mut self) -> Result<Vec<TxMessage>, LinkError> {
        Ok(self.buffer_pool.borrow())
    }

    fn send(&mut self, tx: Vec<TxMessage>) -> Result<(), LinkError> {
        self.cpus.iter_mut().for_each(|cpu| {
            cpu.send(&tx);
        });
        self.buffer_pool.return_buffer(tx);
        Ok(())
    }

    fn receive(&mut self, rx: &mut [RxMessage]) -> Result<(), LinkError> {
        self.cpus.iter_mut().for_each(|cpu| {
            cpu.update_with_sys_time(DcSysTime::ZERO);
            rx[cpu.idx()] = cpu.rx();
        });
        Ok(())
    }

    fn is_open(&self) -> bool {
        !self.cpus.is_empty()
    }
}

#[cfg(feature = "async")]
impl autd3_core::link::AsyncLink for Nop {
    async fn open(&mut self, geometry: &Geometry) -> Result<(), LinkError> {
        <Self as Link>::open(self, geometry)
    }

    async fn close(&mut self) -> Result<(), LinkError> {
        <Self as Link>::close(self)
    }

    async fn update(&mut self, geometry: &Geometry) -> Result<(), LinkError> {
        <Self as Link>::update(self, geometry)
    }

    async fn alloc_tx_buffer(&mut self) -> Result<Vec<TxMessage>, LinkError> {
        <Self as Link>::alloc_tx_buffer(self)
    }

    async fn send(&mut self, tx: Vec<TxMessage>) -> Result<(), LinkError> {
        <Self as Link>::send(self, tx)
    }

    async fn receive(&mut self, rx: &mut [RxMessage]) -> Result<(), LinkError> {
        <Self as Link>::receive(self, rx)
    }

    fn is_open(&self) -> bool {
        <Self as Link>::is_open(self)
    }

    fn ensure_is_open(&self) -> Result<(), LinkError> {
        <Self as Link>::ensure_is_open(self)
    }
}