use std::{
    collections::{BTreeMap, VecDeque},
    ffi::c_void,
    fs::File,
    io::{Read, Seek},
    mem::ManuallyDrop,
    sync::{
        atomic::{AtomicU8, Ordering},
        Arc, Mutex, MutexGuard, Weak,
    },
    task::{Context, Poll},
    time::{Duration, Instant},
};

use log::{debug, error, warn};
use rustix::{
    event::epoll::EventFlags,
    fd::{AsFd, AsRawFd, FromRawFd, OwnedFd},
    fs::Timespec,
    io::Errno,
    time::{timerfd_create, timerfd_settime, Itimerspec, TimerfdFlags, TimerfdTimerFlags},
};
use slab::Slab;

use super::{
    errno_to_transfer_error, events,
    usbfs::{self, Urb},
    TransferData,
};

#[cfg(not(target_os = "android"))]
use super::{
    enumeration::{SysfsError, SysfsErrorKind},
    SysfsPath,
};

use crate::{
    bitset::EndpointBitSet,
    descriptors::{
        parse_concatenated_config_descriptors, ConfigurationDescriptor, DeviceDescriptor,
        EndpointDescriptor, TransferType, DESCRIPTOR_LEN_DEVICE,
    },
    maybe_future::{blocking::Blocking, MaybeFuture},
    transfer::{
        internal::{
            notify_completion, take_completed_from_queue, Idle, Notify, Pending, TransferFuture,
        },
        request_type, Buffer, Completion, ControlIn, ControlOut, ControlType, Direction, Recipient,
        TransferError,
    },
    DeviceInfo, Error, ErrorKind, Speed,
};

#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct TimeoutEntry {
    deadline: Instant,
    urb: *mut Urb,
}

unsafe impl Send for TimeoutEntry {}
unsafe impl Sync for TimeoutEntry {}

static DEVICES: Mutex<Slab<Weak<LinuxDevice>>> = Mutex::new(Slab::new());

pub(crate) struct LinuxDevice {
    fd: OwnedFd,
    events_id: usize,

    /// Read from the fd, consists of device descriptor followed by configuration descriptors
    descriptors: Vec<u8>,

    #[cfg(not(target_os = "android"))]
    sysfs: Option<SysfsPath>,

    active_config: AtomicU8,

    timerfd: OwnedFd,
    timeouts: Mutex<BTreeMap<TimeoutEntry, ()>>,
}

impl LinuxDevice {
    #[cfg(not(target_os = "android"))]
    pub(crate) fn from_device_info(
        d: &DeviceInfo,
    ) -> impl MaybeFuture<Output = Result<Arc<LinuxDevice>, Error>> {
        use rustix::fs::{Mode, OFlags};

        let busnum = d.busnum();
        let devnum = d.device_address();
        let sysfs_path = d.path.clone();

        Blocking::new(move || {
            let path = std::path::PathBuf::from(format!("/dev/bus/usb/{busnum:03}/{devnum:03}"));
            let fd = rustix::fs::open(&path, OFlags::RDWR | OFlags::CLOEXEC, Mode::empty())
                .map_err(|e| {
                    match e {
                        Errno::NOENT => {
                            Error::new_os(ErrorKind::Disconnected, "device not found", e)
                        }
                        Errno::PERM => {
                            Error::new_os(ErrorKind::PermissionDenied, "permission denied", e)
                        }
                        e => Error::new_os(ErrorKind::Other, "failed to open device", e),
                    }
                    .log_debug()
                })?;

            Self::create_inner(fd, Some(sysfs_path))
        })
    }

    #[cfg(target_os = "android")]
    pub(crate) fn from_device_info(
        _d: &DeviceInfo,
    ) -> impl MaybeFuture<Output = Result<Arc<LinuxDevice>, Error>> {
        Blocking::new(move || unimplemented!())
    }

    pub(crate) fn from_fd(
        fd: OwnedFd,
    ) -> impl MaybeFuture<Output = Result<Arc<LinuxDevice>, Error>> {
        Blocking::new(move || {
            debug!("Wrapping fd {} as usbfs device", fd.as_raw_fd());
            Self::create_inner(
                fd,
                #[cfg(not(target_os = "android"))]
                None,
            )
        })
    }

    pub(crate) fn create_inner(
        fd: OwnedFd,
        #[cfg(not(target_os = "android"))] sysfs: Option<SysfsPath>,
    ) -> Result<Arc<LinuxDevice>, Error> {
        let descriptors = read_all_from_fd(&fd).map_err(|e| {
            Error::new_io(ErrorKind::Other, "failed to read descriptors", e).log_error()
        })?;

        let Some(_) = DeviceDescriptor::new(&descriptors) else {
            return Err(Error::new(ErrorKind::Other, "invalid device descriptor"));
        };

        #[cfg(not(target_os = "android"))]
        let active_config: u8 = if let Some(sysfs) = sysfs.as_ref() {
            match sysfs.read_attr("bConfigurationValue") {
                Ok(v) => v,
                // Linux returns an empty string when the device is unconfigured.
                // We'll assume all parse errors are the empty string.
                Err(SysfsError(_, SysfsErrorKind::Parse(_))) => 0,

                Err(e) => {
                    warn!("failed to read sysfs bConfigurationValue: {e}");
                    return Err(Error::new(
                        ErrorKind::Other,
                        "failed to read sysfs bConfigurationValue",
                    ));
                }
            }
        } else {
            guess_active_configuration(&fd, &descriptors)
        };

        #[cfg(target_os = "android")]
        let active_config = guess_active_configuration(&fd, &descriptors);

        let timerfd = timerfd_create(
            rustix::time::TimerfdClockId::Monotonic,
            TimerfdFlags::CLOEXEC | TimerfdFlags::NONBLOCK,
        )
        .map_err(|e| Error::new_os(ErrorKind::Other, "failed to create timerfd", e).log_error())?;

        let arc = Arc::new_cyclic(|weak| {
            let events_id = DEVICES.lock().unwrap().insert(weak.clone());
            LinuxDevice {
                fd,
                events_id,
                descriptors,
                #[cfg(not(target_os = "android"))]
                sysfs,
                active_config: AtomicU8::new(active_config),
                timerfd,
                timeouts: Mutex::new(BTreeMap::new()),
            }
        });

        debug!(
            "Opened device fd={} with id {}",
            arc.fd.as_raw_fd(),
            arc.events_id
        );

        events::register_fd(
            arc.fd.as_fd(),
            events::Tag::Device(arc.events_id),
            EventFlags::OUT,
        )?;

        events::register_fd(
            arc.timerfd.as_fd(),
            events::Tag::DeviceTimer(arc.events_id),
            EventFlags::IN,
        )?;

        Ok(arc)
    }

    pub(crate) fn handle_usb_epoll(id: usize) {
        let device = DEVICES.lock().unwrap().get(id).and_then(|w| w.upgrade());
        if let Some(device) = device {
            device.handle_events();
        }
    }

    fn handle_events(&self) {
        debug!("Handling events for device {}", self.events_id);
        match usbfs::reap_urb_ndelay(&self.fd) {
            Ok(urb) => {
                let transfer_data: *mut TransferData = unsafe { &(*urb) }.usercontext.cast();

                {
                    let transfer = unsafe { &*transfer_data };
                    debug_assert!(transfer.urb_ptr() == urb);
                    debug!(
                        "URB {:?} for ep {:x} completed, status={} actual_length={}",
                        transfer.urb_ptr(),
                        transfer.urb().endpoint,
                        transfer.urb().status,
                        transfer.urb().actual_length
                    );

                    if let Some(deadline) = transfer.deadline {
                        let mut timeouts = self.timeouts.lock().unwrap();
                        timeouts.remove(&TimeoutEntry { deadline, urb });
                        self.update_timeouts(timeouts, Instant::now());
                    }
                };

                // SAFETY: pointer came from submit via kernel and we're now done with it
                unsafe { notify_completion::<super::TransferData>(transfer_data) }
            }
            Err(Errno::AGAIN) => {}
            Err(Errno::NODEV) => {
                debug!("Device {} disconnected", self.events_id);

                // epoll returns events continuously on a disconnected device, and REAPURB
                // only returns ENODEV after all events are received, so unregister to
                // keep the event thread from spinning because we won't receive further events.
                // The drop impl will try to unregister again, but that's ok.
                events::unregister_fd(self.fd.as_fd());
            }
            Err(e) => {
                error!("Unexpected error {e} from REAPURBNDELAY");
            }
        }
    }

    pub(crate) fn handle_timer_epoll(id: usize) {
        let device = DEVICES.lock().unwrap().get(id).and_then(|w| w.upgrade());
        if let Some(device) = device {
            device.handle_timeouts();
        }
    }

    fn handle_timeouts(&self) {
        debug!("Handling timeouts for device {}", self.events_id);
        let now = Instant::now();

        rustix::io::read(self.timerfd.as_fd(), &mut [0u8; 8]).ok();

        let mut timeouts = self.timeouts.lock().unwrap();
        while let Some(entry) = timeouts.first_entry() {
            if entry.key().deadline > now {
                break;
            }

            let urb = entry.remove_entry().0.urb;

            unsafe {
                match usbfs::discard_urb(&self.fd, urb) {
                    Ok(()) => debug!("Cancelled URB {urb:?} after timeout"),
                    Err(e) => debug!("Failed to cancel timed out URB {urb:?}: {e}"),
                }
            }
        }

        self.update_timeouts(timeouts, now);
    }

    fn update_timeouts(&self, timeouts: MutexGuard<BTreeMap<TimeoutEntry, ()>>, now: Instant) {
        const TIMESPEC_ZERO: Timespec = Timespec {
            tv_sec: 0,
            tv_nsec: 0,
        };

        let next = if let Some((TimeoutEntry { deadline, .. }, _)) = timeouts.first_key_value() {
            let duration = deadline
                .checked_duration_since(now)
                .unwrap_or(Duration::from_nanos(1));
            log::debug!("Next timeout in {duration:?}");
            Timespec {
                tv_sec: duration.as_secs() as i64,
                tv_nsec: duration.subsec_nanos() as i64,
            }
        } else {
            TIMESPEC_ZERO
        };

        timerfd_settime(
            self.timerfd.as_fd(),
            TimerfdTimerFlags::empty(),
            &Itimerspec {
                it_interval: TIMESPEC_ZERO,
                it_value: next,
            },
        )
        .inspect_err(|e| {
            log::error!("Failed to set timerfd: {e}");
        })
        .ok();
    }

    pub(crate) fn device_descriptor(&self) -> DeviceDescriptor {
        DeviceDescriptor::new(&self.descriptors).unwrap()
    }

    pub(crate) fn configuration_descriptors(
        &self,
    ) -> impl Iterator<Item = ConfigurationDescriptor<'_>> {
        parse_concatenated_config_descriptors(&self.descriptors[DESCRIPTOR_LEN_DEVICE as usize..])
    }

    pub(crate) fn active_configuration_value(&self) -> u8 {
        #[cfg(not(target_os = "android"))]
        if let Some(sysfs) = self.sysfs.as_ref() {
            match sysfs.read_attr("bConfigurationValue") {
                Ok(v) => {
                    self.active_config.store(v, Ordering::SeqCst);
                    return v;
                }
                Err(SysfsError(_, SysfsErrorKind::Parse(_))) => {
                    self.active_config.store(0, Ordering::SeqCst);
                    return 0;
                }
                Err(e) => {
                    error!("Failed to read sysfs bConfigurationValue: {e}, using cached value");
                }
            }
        }
        self.active_config.load(Ordering::SeqCst)
    }

    pub(crate) fn set_configuration(
        self: Arc<Self>,
        configuration: u8,
    ) -> impl MaybeFuture<Output = Result<(), Error>> {
        Blocking::new(move || {
            usbfs::set_configuration(&self.fd, configuration).map_err(|e| match e {
                Errno::INVAL => Error::new_os(ErrorKind::NotFound, "configuration not found", e),
                Errno::BUSY => Error::new_os(ErrorKind::Busy, "device is busy", e),
                Errno::NODEV => Error::new_os(ErrorKind::Disconnected, "device disconnected", e),
                _ => Error::new_os(ErrorKind::Other, "failed to set configuration", e),
            })?;
            self.active_config.store(configuration, Ordering::SeqCst);
            Ok(())
        })
    }

    pub(crate) fn reset(self: Arc<Self>) -> impl MaybeFuture<Output = Result<(), Error>> {
        Blocking::new(move || {
            usbfs::reset(&self.fd).map_err(|e| match e {
                Errno::BUSY => Error::new_os(ErrorKind::Busy, "device is busy", e),
                Errno::NODEV => Error::new_os(ErrorKind::Disconnected, "device disconnected", e),
                _ => Error::new_os(ErrorKind::Other, "failed to reset device", e),
            })
        })
    }

    pub fn control_in(
        self: Arc<Self>,
        data: ControlIn,
        timeout: Duration,
    ) -> impl MaybeFuture<Output = Result<Vec<u8>, TransferError>> {
        let t = TransferData::new_control_in(data);
        TransferFuture::new(t, |t| self.submit_timeout(t, timeout)).map(move |t| {
            drop(self); // ensure device stays alive
            t.status()?;
            Ok(t.control_in_data().to_owned())
        })
    }

    pub fn control_out(
        self: Arc<Self>,
        data: ControlOut,
        timeout: Duration,
    ) -> impl MaybeFuture<Output = Result<(), TransferError>> {
        let t = TransferData::new_control_out(data);
        TransferFuture::new(t, |t| self.submit_timeout(t, timeout)).map(move |t| {
            drop(self); // ensure device stays alive
            t.status()?;
            Ok(())
        })
    }

    fn handle_claim_interface_result(
        self: Arc<Self>,
        interface_number: u8,
        result: Result<(), Errno>,
        reattach: bool,
    ) -> Result<Arc<LinuxInterface>, Error> {
        result.map_err(|e| {
            match e {
                Errno::INVAL | Errno::NOENT => {
                    Error::new_os(ErrorKind::NotFound, "interface not found", e)
                }
                Errno::BUSY => Error::new_os(ErrorKind::Busy, "interface is busy", e),
                Errno::NODEV => Error::new_os(ErrorKind::Disconnected, "device disconnected", e),
                _ => Error::new_os(ErrorKind::Other, "failed to claim interface", e),
            }
            .log_error()
        })?;
        debug!(
            "Claimed interface {interface_number} on device id {dev}",
            dev = self.events_id
        );
        Ok(Arc::new(LinuxInterface {
            device: self,
            interface_number,
            reattach,
            state: Mutex::new(Default::default()),
        }))
    }

    pub(crate) fn claim_interface(
        self: Arc<Self>,
        interface_number: u8,
    ) -> impl MaybeFuture<Output = Result<Arc<LinuxInterface>, Error>> {
        Blocking::new(move || {
            let result = usbfs::claim_interface(&self.fd, interface_number);
            self.handle_claim_interface_result(interface_number, result, false)
        })
    }

    pub(crate) fn detach_and_claim_interface(
        self: Arc<Self>,
        interface_number: u8,
    ) -> impl MaybeFuture<Output = Result<Arc<LinuxInterface>, Error>> {
        Blocking::new(move || {
            let result = usbfs::detach_and_claim_interface(&self.fd, interface_number);
            self.handle_claim_interface_result(interface_number, result, true)
        })
    }

    #[cfg(target_os = "linux")]
    pub(crate) fn detach_kernel_driver(
        self: &Arc<Self>,
        interface_number: u8,
    ) -> Result<(), Error> {
        usbfs::detach_kernel_driver(&self.fd, interface_number).map_err(|e| match e {
            Errno::INVAL => Error::new_os(ErrorKind::NotFound, "interface not found", e),
            Errno::NODEV => Error::new_os(ErrorKind::Disconnected, "device disconnected", e),
            Errno::NODATA => Error::new_os(ErrorKind::Other, "no kernel driver attached", e),
            _ => Error::new_os(ErrorKind::Other, "failed to detach kernel driver", e),
        })
    }

    #[cfg(target_os = "linux")]
    pub(crate) fn attach_kernel_driver(
        self: &Arc<Self>,
        interface_number: u8,
    ) -> Result<(), Error> {
        usbfs::attach_kernel_driver(&self.fd, interface_number).map_err(|e| match e {
            Errno::INVAL => Error::new_os(ErrorKind::NotFound, "interface not found", e),
            Errno::NODEV => Error::new_os(ErrorKind::Disconnected, "device disconnected", e),
            Errno::BUSY => Error::new_os(ErrorKind::Busy, "kernel driver already attached", e),
            _ => Error::new_os(ErrorKind::Other, "failed to attach kernel driver", e),
        })
    }

    pub(crate) fn submit(&self, transfer: Idle<TransferData>) -> Pending<TransferData> {
        let len = transfer.urb().buffer_length;
        let pending = transfer.pre_submit();
        let urb = pending.urb_ptr();

        // SAFETY: We got the urb from `Idle<TransferData>`, which always points to
        // a valid URB with valid buffers, which is not already pending
        unsafe {
            let ep = (*urb).endpoint;
            (*urb).usercontext = pending.as_ptr().cast();
            if let Err(e) = usbfs::submit_urb(&self.fd, urb) {
                // SAFETY: Transfer was not submitted. We still own the transfer
                // and can write to the URB and complete it in place of the handler.
                let u = &mut *urb;
                debug!("Failed to submit URB {urb:?}: {len} bytes on ep {ep:x}: {e} {u:?}");
                u.actual_length = 0;
                u.status = e.raw_os_error();
                notify_completion::<super::TransferData>(pending.as_ptr().cast());
            } else {
                debug!("Submitted URB {urb:?}: {len} bytes on ep {ep:x}");
            }
        };

        pending
    }

    fn submit_timeout(
        &self,
        mut transfer: Idle<TransferData>,
        timeout: Duration,
    ) -> Pending<TransferData> {
        let urb = transfer.urb_ptr();
        let now = Instant::now();
        let deadline = now + timeout;
        transfer.deadline = Some(deadline);

        // Hold the lock across `submit`, so that it can't complete before we
        // insert the timeout entry.
        let mut timeouts = self.timeouts.lock().unwrap();

        let r = self.submit(transfer);

        // This can only be false if submit failed, because we hold the timeouts lock
        // and would block the completion handler.
        if !r.is_complete() {
            timeouts.insert(TimeoutEntry { deadline, urb }, ());
            self.update_timeouts(timeouts, now);
        }

        r
    }

    pub(crate) fn cancel(&self, transfer: &mut Pending<TransferData>) {
        let urb = transfer.urb_ptr();
        unsafe {
            if let Err(e) = usbfs::discard_urb(&self.fd, urb) {
                debug!("Failed to cancel URB {urb:?}: {e}");
            } else {
                debug!("Requested cancellation of URB {urb:?}");
            }
        }
    }

    pub(crate) fn speed(&self) -> Option<Speed> {
        usbfs::get_speed(&self.fd)
            .inspect_err(|e| log::error!("USBDEVFS_GET_SPEED failed: {e}"))
            .ok()
            .and_then(|raw_speed| match raw_speed {
                1 => Some(Speed::Low),
                2 => Some(Speed::Full),
                3 => Some(Speed::High),
                // 4 is wireless USB, but we don't support it
                5 => Some(Speed::Super),
                6 => Some(Speed::SuperPlus),
                _ => None,
            })
    }
}

fn read_all_from_fd(fd: &OwnedFd) -> Result<Vec<u8>, std::io::Error> {
    let mut file = unsafe { ManuallyDrop::new(File::from_raw_fd(fd.as_raw_fd())) };
    file.seek(std::io::SeekFrom::Start(0))?;
    let mut buf = Vec::new();
    file.read_to_end(&mut buf)?;
    Ok(buf)
}

/// Try a request to get the active configuration or fall back to a guess.
fn guess_active_configuration(fd: &OwnedFd, descriptors: &[u8]) -> u8 {
    request_configuration(fd).unwrap_or_else(|()| {
        let mut config_descriptors =
            parse_concatenated_config_descriptors(&descriptors[DESCRIPTOR_LEN_DEVICE as usize..]);

        if let Some(config) = config_descriptors.next() {
            config.configuration_value()
        } else {
            error!("no configurations for device fd {}", fd.as_raw_fd());
            0
        }
    })
}

/// Get the active configuration with a blocking request to the device.
fn request_configuration(fd: &OwnedFd) -> Result<u8, ()> {
    const REQUEST_GET_CONFIGURATION: u8 = 0x08;

    let mut dst = [0u8; 1];
    let r = usbfs::control(
        fd,
        usbfs::CtrlTransfer {
            bRequestType: request_type(Direction::In, ControlType::Standard, Recipient::Device),
            bRequest: REQUEST_GET_CONFIGURATION,
            wValue: 0,
            wIndex: 0,
            wLength: dst.len() as u16,
            timeout: 50,
            data: &mut dst[0] as *mut u8 as *mut c_void,
        },
    );

    match r {
        Ok(1) => {
            let active_config = dst[0];
            debug!("Obtained active configuration for fd {} from GET_CONFIGURATION request: {active_config}", fd.as_raw_fd());
            Ok(active_config)
        }
        Ok(n) => {
            warn!("GET_CONFIGURATION request returned unexpected length: {n}, expected 1");
            Err(())
        }
        Err(e) => {
            warn!(
                "GET_CONFIGURATION request failed: {:?}",
                errno_to_transfer_error(e)
            );
            Err(())
        }
    }
}

impl Drop for LinuxDevice {
    fn drop(&mut self) {
        debug!("Closing device {}", self.events_id);
        events::unregister_fd(self.fd.as_fd());
        events::unregister_fd(self.timerfd.as_fd());
        DEVICES.lock().unwrap().remove(self.events_id);
    }
}

pub(crate) struct LinuxInterface {
    pub(crate) interface_number: u8,
    pub(crate) device: Arc<LinuxDevice>,
    reattach: bool,
    state: Mutex<InterfaceState>,
}

#[derive(Default)]
struct InterfaceState {
    endpoints: EndpointBitSet,
    alt_setting: u8,
}

impl LinuxInterface {
    pub fn control_in(
        &self,
        data: ControlIn,
        timeout: Duration,
    ) -> impl MaybeFuture<Output = Result<Vec<u8>, TransferError>> {
        self.device.clone().control_in(data, timeout)
    }

    pub fn control_out(
        &self,
        data: ControlOut,
        timeout: Duration,
    ) -> impl MaybeFuture<Output = Result<(), TransferError>> {
        self.device.clone().control_out(data, timeout)
    }

    pub fn get_alt_setting(&self) -> u8 {
        self.state.lock().unwrap().alt_setting
    }

    pub fn set_alt_setting(
        self: Arc<Self>,
        alt_setting: u8,
    ) -> impl MaybeFuture<Output = Result<(), Error>> {
        Blocking::new(move || {
            let mut state = self.state.lock().unwrap();
            if !state.endpoints.is_empty() {
                return Err(Error::new(
                    ErrorKind::Busy,
                    "can't change alternate setting while endpoints are in use",
                ));
            }
            usbfs::set_interface(&self.device.fd, self.interface_number, alt_setting).map_err(
                |e| match e {
                    Errno::INVAL => {
                        Error::new_os(ErrorKind::NotFound, "alternate setting not found", e)
                    }
                    Errno::NODEV => {
                        Error::new_os(ErrorKind::Disconnected, "device disconnected", e)
                    }
                    _ => Error::new_os(ErrorKind::Other, "failed to set alternate setting", e),
                },
            )?;
            debug!(
                "Set interface {} alt setting to {alt_setting}",
                self.interface_number
            );
            state.alt_setting = alt_setting;
            Ok(())
        })
    }

    pub fn endpoint(
        self: &Arc<Self>,
        descriptor: EndpointDescriptor,
    ) -> Result<LinuxEndpoint, Error> {
        let address = descriptor.address();
        let ep_type = descriptor.transfer_type();
        let max_packet_size = descriptor.max_packet_size();

        let mut state = self.state.lock().unwrap();

        if state.endpoints.is_set(address) {
            return Err(Error::new(ErrorKind::Busy, "endpoint already in use"));
        }
        state.endpoints.set(address);

        Ok(LinuxEndpoint {
            inner: Arc::new(EndpointInner {
                address,
                ep_type,
                interface: self.clone(),
                notify: Notify::new(),
            }),
            max_packet_size,
            pending: VecDeque::new(),
            idle_transfer: None,
        })
    }
}

impl Drop for LinuxInterface {
    fn drop(&mut self) {
        let res = usbfs::release_interface(&self.device.fd, self.interface_number);
        debug!(
            "Released interface {} on device {}: {res:?}",
            self.interface_number, self.device.events_id
        );

        if res.is_ok() && self.reattach {
            let res = usbfs::attach_kernel_driver(&self.device.fd, self.interface_number);
            debug!(
                "Reattached kernel drivers for interface {} on device {}: {res:?}",
                self.interface_number, self.device.events_id
            );
        }
    }
}

pub(crate) struct LinuxEndpoint {
    inner: Arc<EndpointInner>,

    pub(crate) max_packet_size: usize,

    /// A queue of pending transfers, expected to complete in order
    pending: VecDeque<Pending<super::TransferData>>,

    idle_transfer: Option<Idle<TransferData>>,
}

struct EndpointInner {
    interface: Arc<LinuxInterface>,
    address: u8,
    ep_type: TransferType,
    notify: Notify,
}

impl LinuxEndpoint {
    pub(crate) fn endpoint_address(&self) -> u8 {
        self.inner.address
    }

    pub(crate) fn pending(&self) -> usize {
        self.pending.len()
    }

    pub(crate) fn cancel_all(&mut self) {
        // Cancel transfers in reverse order to ensure subsequent transfers
        // can't complete out of order while we're going through them.
        for transfer in self.pending.iter_mut().rev() {
            self.inner.interface.device.cancel(transfer);
        }
    }

    fn get_transfer(&mut self) -> Idle<TransferData> {
        self.idle_transfer.take().unwrap_or_else(|| {
            Idle::new(
                self.inner.clone(),
                super::TransferData::new(self.inner.address, self.inner.ep_type),
            )
        })
    }

    pub(crate) fn submit(&mut self, data: Buffer) {
        let mut transfer = self.get_transfer();
        transfer.set_buffer(data);
        self.pending
            .push_back(self.inner.interface.device.submit(transfer));
    }

    pub(crate) fn submit_err(&mut self, data: Buffer, error: TransferError) {
        assert_eq!(error, TransferError::InvalidArgument);
        let mut transfer = self.get_transfer();
        transfer.set_buffer(data);
        transfer.urb_mut().status = Errno::INVAL.raw_os_error();
        self.pending.push_back(transfer.simulate_complete());
    }

    pub(crate) fn poll_next_complete(&mut self, cx: &mut Context) -> Poll<Completion> {
        self.inner.notify.subscribe(cx);
        if let Some(mut transfer) = take_completed_from_queue(&mut self.pending) {
            let completion = transfer.take_completion();
            self.idle_transfer = Some(transfer);
            Poll::Ready(completion)
        } else {
            Poll::Pending
        }
    }

    pub(crate) fn wait_next_complete(&mut self, timeout: Duration) -> Option<Completion> {
        self.inner.notify.wait_timeout(timeout, || {
            take_completed_from_queue(&mut self.pending).map(|mut transfer| {
                let completion = transfer.take_completion();
                self.idle_transfer = Some(transfer);
                completion
            })
        })
    }

    pub(crate) fn clear_halt(&self) -> impl MaybeFuture<Output = Result<(), Error>> {
        let inner = self.inner.clone();
        Blocking::new(move || {
            let endpoint = inner.address;
            debug!("Clear halt, endpoint {endpoint:02x}");
            usbfs::clear_halt(&inner.interface.device.fd, endpoint).map_err(|e| match e {
                Errno::NODEV => Error::new_os(ErrorKind::Disconnected, "device disconnected", e),
                _ => Error::new_os(ErrorKind::Other, "failed to clear halt", e),
            })
        })
    }

    pub(crate) fn allocate(&self, len: usize) -> Result<Buffer, Errno> {
        Buffer::mmap(&self.inner.interface.device.fd, len).inspect_err(|e| {
            warn!(
                "Failed to allocate zero-copy buffer of length {len} for endpoint {}: {e}",
                self.inner.address
            );
        })
    }
}

impl Drop for LinuxEndpoint {
    fn drop(&mut self) {
        if !self.pending.is_empty() {
            debug!(
                "Dropping endpoint {:02x} with {} pending transfers",
                self.inner.address,
                self.pending.len()
            );
            self.cancel_all();
        }
    }
}

impl AsRef<Notify> for EndpointInner {
    fn as_ref(&self) -> &Notify {
        &self.notify
    }
}

impl Drop for EndpointInner {
    fn drop(&mut self) {
        let mut state = self.interface.state.lock().unwrap();
        state.endpoints.clear(self.address);
    }
}
