// SPDX-License-Identifier: MIT

use anyhow::Context;
use netlink_packet_utils::{
    nla::{self, DefaultNla, NlaBuffer, NlasIterator},
    traits::{Emitable, Parseable},
    DecodeError,
};

use super::{inet, inet6};
use crate::constants::*;

#[derive(Clone, Eq, PartialEq, Debug)]
#[non_exhaustive]
pub enum AfSpecInet {
    Unspec(Vec<u8>),
    Unix(Vec<u8>),
    Ax25(Vec<u8>),
    Ipx(Vec<u8>),
    AppleTalk(Vec<u8>),
    Netrom(Vec<u8>),
    Bridge(Vec<u8>),
    AtmPvc(Vec<u8>),
    X25(Vec<u8>),
    Inet(Vec<inet::Inet>),
    Inet6(Vec<inet6::Inet6>),
    Rose(Vec<u8>),
    DecNet(Vec<u8>),
    NetbEui(Vec<u8>),
    Security(Vec<u8>),
    Key(Vec<u8>),
    Netlink(Vec<u8>),
    Packet(Vec<u8>),
    Ash(Vec<u8>),
    EcoNet(Vec<u8>),
    AtmSvc(Vec<u8>),
    Rds(Vec<u8>),
    Sna(Vec<u8>),
    Irda(Vec<u8>),
    Pppox(Vec<u8>),
    WanPipe(Vec<u8>),
    Llc(Vec<u8>),
    Can(Vec<u8>),
    Tipc(Vec<u8>),
    Bluetooth(Vec<u8>),
    Iucv(Vec<u8>),
    RxRpc(Vec<u8>),
    Isdn(Vec<u8>),
    Phonet(Vec<u8>),
    Ieee802154(Vec<u8>),
    Caif(Vec<u8>),
    Alg(Vec<u8>),
    Other(DefaultNla),
}

impl nla::Nla for AfSpecInet {
    #[rustfmt::skip]
    fn value_len(&self) -> usize {
        use self::AfSpecInet::*;
        match *self {
            Unspec(ref bytes)
                | Unix(ref bytes)
                | Ax25(ref bytes)
                | Ipx(ref bytes)
                | AppleTalk(ref bytes)
                | Netrom(ref bytes)
                | Bridge(ref bytes)
                | AtmPvc(ref bytes)
                | X25(ref bytes)
                | Rose(ref bytes)
                | DecNet(ref bytes)
                | NetbEui(ref bytes)
                | Security(ref bytes)
                | Key(ref bytes)
                | Netlink(ref bytes)
                | Packet(ref bytes)
                | Ash(ref bytes)
                | EcoNet(ref bytes)
                | AtmSvc(ref bytes)
                | Rds(ref bytes)
                | Sna(ref bytes)
                | Irda(ref bytes)
                | Pppox(ref bytes)
                | WanPipe(ref bytes)
                | Llc(ref bytes)
                | Can(ref bytes)
                | Tipc(ref bytes)
                | Bluetooth(ref bytes)
                | Iucv(ref bytes)
                | RxRpc(ref bytes)
                | Isdn(ref bytes)
                | Phonet(ref bytes)
                | Ieee802154(ref bytes)
                | Caif(ref bytes)
                | Alg(ref bytes)
                => bytes.len(),
            Inet6(ref nlas) => nlas.as_slice().buffer_len(),
            Inet(ref nlas) =>  nlas.as_slice().buffer_len(),
            Other(ref nla) => nla.value_len(),
        }
    }

    #[rustfmt::skip]
    fn emit_value(&self, buffer: &mut [u8]) {
        use self::AfSpecInet::*;
        match *self {
            Unspec(ref bytes)
                | Unix(ref bytes)
                | Ax25(ref bytes)
                | Ipx(ref bytes)
                | AppleTalk(ref bytes)
                | Netrom(ref bytes)
                | Bridge(ref bytes)
                | AtmPvc(ref bytes)
                | X25(ref bytes)
                | Rose(ref bytes)
                | DecNet(ref bytes)
                | NetbEui(ref bytes)
                | Security(ref bytes)
                | Key(ref bytes)
                | Netlink(ref bytes)
                | Packet(ref bytes)
                | Ash(ref bytes)
                | EcoNet(ref bytes)
                | AtmSvc(ref bytes)
                | Rds(ref bytes)
                | Sna(ref bytes)
                | Irda(ref bytes)
                | Pppox(ref bytes)
                | WanPipe(ref bytes)
                | Llc(ref bytes)
                | Can(ref bytes)
                | Tipc(ref bytes)
                | Bluetooth(ref bytes)
                | Iucv(ref bytes)
                | RxRpc(ref bytes)
                | Isdn(ref bytes)
                | Phonet(ref bytes)
                | Ieee802154(ref bytes)
                | Caif(ref bytes)
                | Alg(ref bytes)
                => buffer[..bytes.len()].copy_from_slice(bytes.as_slice()),
            Inet6(ref nlas) => nlas.as_slice().emit(buffer),
            Inet(ref nlas) => nlas.as_slice().emit(buffer),
            Other(ref nla)  => nla.emit_value(buffer),
        }
    }

    fn kind(&self) -> u16 {
        use self::AfSpecInet::*;
        match *self {
            Inet(_) => AF_INET,
            Unspec(_) => AF_UNSPEC,
            Unix(_) => AF_UNIX,
            Ax25(_) => AF_AX25,
            Ipx(_) => AF_IPX,
            AppleTalk(_) => AF_APPLETALK,
            Netrom(_) => AF_NETROM,
            Bridge(_) => AF_BRIDGE,
            AtmPvc(_) => AF_ATMPVC,
            X25(_) => AF_X25,
            Inet6(_) => AF_INET6,
            Rose(_) => AF_ROSE,
            DecNet(_) => AF_DECNET,
            NetbEui(_) => AF_NETBEUI,
            Security(_) => AF_SECURITY,
            Key(_) => AF_KEY,
            Netlink(_) => AF_NETLINK,
            Packet(_) => AF_PACKET,
            Ash(_) => AF_ASH,
            EcoNet(_) => AF_ECONET,
            AtmSvc(_) => AF_ATMSVC,
            Rds(_) => AF_RDS,
            Sna(_) => AF_SNA,
            Irda(_) => AF_IRDA,
            Pppox(_) => AF_PPPOX,
            WanPipe(_) => AF_WANPIPE,
            Llc(_) => AF_LLC,
            Can(_) => AF_CAN,
            Tipc(_) => AF_TIPC,
            Bluetooth(_) => AF_BLUETOOTH,
            Iucv(_) => AF_IUCV,
            RxRpc(_) => AF_RXRPC,
            Isdn(_) => AF_ISDN,
            Phonet(_) => AF_PHONET,
            Ieee802154(_) => AF_IEEE802154,
            Caif(_) => AF_CAIF,
            Alg(_) => AF_ALG,
            Other(ref nla) => nla.kind(),
        }
    }
}

impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for AfSpecInet {
    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
        use self::AfSpecInet::*;

        let payload = buf.value();
        Ok(match buf.kind() {
            AF_UNSPEC => Unspec(payload.to_vec()),
            AF_INET => {
                let mut nlas = vec![];
                for nla in NlasIterator::new(payload) {
                    let nla = nla.context("invalid AF_INET value")?;
                    nlas.push(
                        inet::Inet::parse(&nla)
                            .context("invalid AF_INET value")?,
                    );
                }
                Inet(nlas)
            }
            AF_INET6 => {
                let mut nlas = vec![];
                for nla in NlasIterator::new(payload) {
                    let nla = nla.context("invalid AF_INET6 value")?;
                    nlas.push(
                        inet6::Inet6::parse(&nla)
                            .context("invalid AF_INET6 value")?,
                    );
                }
                Inet6(nlas)
            }
            AF_UNIX => Unix(payload.to_vec()),
            AF_AX25 => Ax25(payload.to_vec()),
            AF_IPX => Ipx(payload.to_vec()),
            AF_APPLETALK => AppleTalk(payload.to_vec()),
            AF_NETROM => Netrom(payload.to_vec()),
            AF_BRIDGE => Bridge(payload.to_vec()),
            AF_ATMPVC => AtmPvc(payload.to_vec()),
            AF_X25 => X25(payload.to_vec()),
            AF_ROSE => Rose(payload.to_vec()),
            AF_DECNET => DecNet(payload.to_vec()),
            AF_NETBEUI => NetbEui(payload.to_vec()),
            AF_SECURITY => Security(payload.to_vec()),
            AF_KEY => Key(payload.to_vec()),
            AF_NETLINK => Netlink(payload.to_vec()),
            AF_PACKET => Packet(payload.to_vec()),
            AF_ASH => Ash(payload.to_vec()),
            AF_ECONET => EcoNet(payload.to_vec()),
            AF_ATMSVC => AtmSvc(payload.to_vec()),
            AF_RDS => Rds(payload.to_vec()),
            AF_SNA => Sna(payload.to_vec()),
            AF_IRDA => Irda(payload.to_vec()),
            AF_PPPOX => Pppox(payload.to_vec()),
            AF_WANPIPE => WanPipe(payload.to_vec()),
            AF_LLC => Llc(payload.to_vec()),
            AF_CAN => Can(payload.to_vec()),
            AF_TIPC => Tipc(payload.to_vec()),
            AF_BLUETOOTH => Bluetooth(payload.to_vec()),
            AF_IUCV => Iucv(payload.to_vec()),
            AF_RXRPC => RxRpc(payload.to_vec()),
            AF_ISDN => Isdn(payload.to_vec()),
            AF_PHONET => Phonet(payload.to_vec()),
            AF_IEEE802154 => Ieee802154(payload.to_vec()),
            AF_CAIF => Caif(payload.to_vec()),
            AF_ALG => Alg(payload.to_vec()),
            kind => Other(
                DefaultNla::parse(buf)
                    .context(format!("Unknown NLA type {kind}"))?,
            ),
        })
    }
}
