use crate::{
    filesystem::epoll::EPollEventType,
    net::socket::netlink::{
        addr::{multicast::GroupIdSet, NetlinkSocketAddr},
        receiver::MessageQueue,
        table::BoundHandle,
    },
    process::{cred::Cred, namespace::net_namespace::NetNamespace},
};
use alloc::fmt::Debug;
use alloc::sync::Arc;
use system_error::SystemError;

#[derive(Debug)]
pub struct BoundNetlink<Message: 'static + Debug> {
    pub(in crate::net::socket::netlink) handle: BoundHandle<Message>,
    pub(in crate::net::socket::netlink) remote_addr: NetlinkSocketAddr,
    pub(in crate::net::socket::netlink) receive_queue: MessageQueue<Message>,
    pub(in crate::net::socket::netlink) netns: Arc<NetNamespace>,
    pub(in crate::net::socket::netlink) opener_cred: Arc<Cred>,
}

impl<Message: 'static + Debug> BoundNetlink<Message> {
    pub(super) fn new(
        handle: BoundHandle<Message>,
        message_queue: MessageQueue<Message>,
        netns: Arc<NetNamespace>,
        opener_cred: Arc<Cred>,
    ) -> Self {
        Self {
            handle,
            remote_addr: NetlinkSocketAddr::new_unspecified(),
            receive_queue: message_queue,
            netns,
            opener_cred,
        }
    }

    pub fn bind_common(&mut self, endpoint: &NetlinkSocketAddr) -> Result<(), SystemError> {
        if endpoint.port() != self.handle.port() {
            return Err(SystemError::EINVAL);
        }
        let groups = endpoint.groups();
        self.handle.bind_groups(groups);

        Ok(())
    }

    pub fn check_io_events_common(&self) -> EPollEventType {
        let mut events = EPollEventType::EPOLLOUT;

        let receive_queue = self.receive_queue.0.lock();
        if !receive_queue.is_empty() {
            events |= EPollEventType::EPOLLIN;
        }

        events
    }

    pub(super) fn add_groups(&mut self, groups: GroupIdSet) {
        self.handle.add_groups(groups);
    }

    pub(super) fn drop_groups(&mut self, groups: GroupIdSet) {
        self.handle.drop_groups(groups);
    }

    pub fn netns(&self) -> Arc<NetNamespace> {
        self.netns.clone()
    }

    pub fn opener_cred(&self) -> Arc<Cred> {
        self.opener_cred.clone()
    }
}
