#![allow(dead_code)]

use bitflags::bitflags;
use byteorder::{ByteOrder, NetworkEndian};
use core::iter;
use core::iter::Iterator;

use super::{Error, Result};
#[cfg(feature = "proto-ipv4")]
use crate::wire::Ipv4Address;
#[cfg(feature = "proto-ipv6")]
use crate::wire::Ipv6Address;

enum_with_unknown! {
    /// DNS OpCodes
    pub enum Opcode(u8) {
        Query  = 0x00,
        Status = 0x01,
    }
}
enum_with_unknown! {
    /// DNS OpCodes
    pub enum Rcode(u8) {
        NoError  = 0x00,
        FormErr  = 0x01,
        ServFail = 0x02,
        NXDomain = 0x03,
        NotImp   = 0x04,
        Refused  = 0x05,
        YXDomain = 0x06,
        YXRRSet  = 0x07,
        NXRRSet  = 0x08,
        NotAuth  = 0x09,
        NotZone  = 0x0a,
    }
}

enum_with_unknown! {
    /// DNS record types
    pub enum Type(u16) {
        A     = 0x0001,
        Ns    = 0x0002,
        Cname = 0x0005,
        Soa   = 0x0006,
        Aaaa  = 0x001c,
    }
}

bitflags! {
    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
    pub struct Flags: u16 {
        const RESPONSE            = 0b1000_0000_0000_0000;
        const AUTHORITATIVE       = 0b0000_0100_0000_0000;
        const TRUNCATED           = 0b0000_0010_0000_0000;
        const RECURSION_DESIRED   = 0b0000_0001_0000_0000;
        const RECURSION_AVAILABLE = 0b0000_0000_1000_0000;
        const AUTHENTIC_DATA      = 0b0000_0000_0010_0000;
        const CHECK_DISABLED      = 0b0000_0000_0001_0000;
    }
}

mod field {
    use crate::wire::field::*;

    pub const ID: Field = 0..2;
    pub const FLAGS: Field = 2..4;
    pub const QDCOUNT: Field = 4..6;
    pub const ANCOUNT: Field = 6..8;
    pub const NSCOUNT: Field = 8..10;
    pub const ARCOUNT: Field = 10..12;

    pub const HEADER_END: usize = 12;
}

// DNS class IN (Internet)
const CLASS_IN: u16 = 1;

/// A read/write wrapper around a DNS packet buffer.
#[derive(Debug, PartialEq, Eq)]
pub struct Packet<T: AsRef<[u8]>> {
    buffer: T,
}

impl<T: AsRef<[u8]>> Packet<T> {
    /// Imbue a raw octet buffer with DNS packet structure.
    pub const fn new_unchecked(buffer: T) -> Packet<T> {
        Packet { buffer }
    }

    /// Shorthand for a combination of [new_unchecked] and [check_len].
    ///
    /// [new_unchecked]: #method.new_unchecked
    /// [check_len]: #method.check_len
    pub fn new_checked(buffer: T) -> Result<Packet<T>> {
        let packet = Self::new_unchecked(buffer);
        packet.check_len()?;
        Ok(packet)
    }

    /// Ensure that no accessor method will panic if called.
    /// Returns `Err(Error)` if the buffer is smaller than
    /// the header length.
    pub fn check_len(&self) -> Result<()> {
        let len = self.buffer.as_ref().len();
        if len < field::HEADER_END {
            Err(Error)
        } else {
            Ok(())
        }
    }

    /// Consume the packet, returning the underlying buffer.
    pub fn into_inner(self) -> T {
        self.buffer
    }

    pub fn payload(&self) -> &[u8] {
        &self.buffer.as_ref()[field::HEADER_END..]
    }

    pub fn transaction_id(&self) -> u16 {
        let field = &self.buffer.as_ref()[field::ID];
        NetworkEndian::read_u16(field)
    }

    pub fn flags(&self) -> Flags {
        let field = &self.buffer.as_ref()[field::FLAGS];
        Flags::from_bits_truncate(NetworkEndian::read_u16(field))
    }

    pub fn opcode(&self) -> Opcode {
        let field = &self.buffer.as_ref()[field::FLAGS];
        let flags = NetworkEndian::read_u16(field);
        Opcode::from((flags >> 11 & 0xF) as u8)
    }

    pub fn rcode(&self) -> Rcode {
        let field = &self.buffer.as_ref()[field::FLAGS];
        let flags = NetworkEndian::read_u16(field);
        Rcode::from((flags & 0xF) as u8)
    }

    pub fn question_count(&self) -> u16 {
        let field = &self.buffer.as_ref()[field::QDCOUNT];
        NetworkEndian::read_u16(field)
    }

    pub fn answer_record_count(&self) -> u16 {
        let field = &self.buffer.as_ref()[field::ANCOUNT];
        NetworkEndian::read_u16(field)
    }

    pub fn authority_record_count(&self) -> u16 {
        let field = &self.buffer.as_ref()[field::NSCOUNT];
        NetworkEndian::read_u16(field)
    }

    pub fn additional_record_count(&self) -> u16 {
        let field = &self.buffer.as_ref()[field::ARCOUNT];
        NetworkEndian::read_u16(field)
    }

    /// Parse part of a name from `bytes`, following pointers if any.
    pub fn parse_name<'a>(&'a self, mut bytes: &'a [u8]) -> impl Iterator<Item = Result<&'a [u8]>> {
        let mut packet = self.buffer.as_ref();

        iter::from_fn(move || loop {
            if bytes.is_empty() {
                return Some(Err(Error));
            }
            match bytes[0] {
                0x00 => return None,
                x if x & 0xC0 == 0x00 => {
                    let len = (x & 0x3F) as usize;
                    if bytes.len() < 1 + len {
                        return Some(Err(Error));
                    }
                    let label = &bytes[1..1 + len];
                    bytes = &bytes[1 + len..];
                    return Some(Ok(label));
                }
                x if x & 0xC0 == 0xC0 => {
                    if bytes.len() < 2 {
                        return Some(Err(Error));
                    }
                    let y = bytes[1];
                    let ptr = ((x & 0x3F) as usize) << 8 | (y as usize);
                    if packet.len() <= ptr {
                        return Some(Err(Error));
                    }

                    // RFC1035 says: "In this scheme, an entire domain name or a list of labels at
                    //      the end of a domain name is replaced with a pointer to a ***prior*** occurrence
                    //      of the same name.
                    //
                    // Is it unclear if this means the pointer MUST point backwards in the packet or not. Either way,
                    // pointers that don't point backwards are never seen in the fields, so use this to check that
                    // there are no pointer loops.

                    // Split packet into parts before and after `ptr`.
                    // parse the part after, keep only the part before in `packet`. This ensure we never
                    // parse the same byte twice, therefore eliminating pointer loops.

                    bytes = &packet[ptr..];
                    packet = &packet[..ptr];
                }
                _ => return Some(Err(Error)),
            }
        })
    }
}

impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
    pub fn payload_mut(&mut self) -> &mut [u8] {
        let data = self.buffer.as_mut();
        &mut data[field::HEADER_END..]
    }

    pub fn set_transaction_id(&mut self, val: u16) {
        let field = &mut self.buffer.as_mut()[field::ID];
        NetworkEndian::write_u16(field, val)
    }

    pub fn set_flags(&mut self, val: Flags) {
        let field = &mut self.buffer.as_mut()[field::FLAGS];
        let mask = Flags::all().bits;
        let old = NetworkEndian::read_u16(field);
        NetworkEndian::write_u16(field, (old & !mask) | val.bits());
    }

    pub fn set_opcode(&mut self, val: Opcode) {
        let field = &mut self.buffer.as_mut()[field::FLAGS];
        let mask = 0x3800;
        let val: u8 = val.into();
        let val = (val as u16) << 11;
        let old = NetworkEndian::read_u16(field);
        NetworkEndian::write_u16(field, (old & !mask) | val);
    }

    pub fn set_question_count(&mut self, val: u16) {
        let field = &mut self.buffer.as_mut()[field::QDCOUNT];
        NetworkEndian::write_u16(field, val)
    }
    pub fn set_answer_record_count(&mut self, val: u16) {
        let field = &mut self.buffer.as_mut()[field::ANCOUNT];
        NetworkEndian::write_u16(field, val)
    }
    pub fn set_authority_record_count(&mut self, val: u16) {
        let field = &mut self.buffer.as_mut()[field::NSCOUNT];
        NetworkEndian::write_u16(field, val)
    }
    pub fn set_additional_record_count(&mut self, val: u16) {
        let field = &mut self.buffer.as_mut()[field::ARCOUNT];
        NetworkEndian::write_u16(field, val)
    }
}

/// Parse part of a name from `bytes`, not following pointers.
/// Returns the unused part of `bytes`, and the pointer offset if the sequence ends with a pointer.
fn parse_name_part<'a>(
    mut bytes: &'a [u8],
    mut f: impl FnMut(&'a [u8]),
) -> Result<(&'a [u8], Option<usize>)> {
    loop {
        let x = *bytes.first().ok_or(Error)?;
        bytes = &bytes[1..];
        match x {
            0x00 => return Ok((bytes, None)),
            x if x & 0xC0 == 0x00 => {
                let len = (x & 0x3F) as usize;
                let label = bytes.get(..len).ok_or(Error)?;
                bytes = &bytes[len..];
                f(label);
            }
            x if x & 0xC0 == 0xC0 => {
                let y = *bytes.first().ok_or(Error)?;
                bytes = &bytes[1..];

                let ptr = ((x & 0x3F) as usize) << 8 | (y as usize);
                return Ok((bytes, Some(ptr)));
            }
            _ => return Err(Error),
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Question<'a> {
    pub name: &'a [u8],
    pub type_: Type,
}

impl<'a> Question<'a> {
    pub fn parse(buffer: &'a [u8]) -> Result<(&'a [u8], Question<'a>)> {
        let (rest, _) = parse_name_part(buffer, |_| ())?;
        let name = &buffer[..buffer.len() - rest.len()];

        if rest.len() < 4 {
            return Err(Error);
        }
        let type_ = NetworkEndian::read_u16(&rest[0..2]).into();
        let class = NetworkEndian::read_u16(&rest[2..4]);
        let rest = &rest[4..];

        if class != CLASS_IN {
            return Err(Error);
        }

        Ok((rest, Question { name, type_ }))
    }

    /// Return the length of a packet that will be emitted from this high-level representation.
    pub const fn buffer_len(&self) -> usize {
        self.name.len() + 4
    }

    /// Emit a high-level representation into a DNS packet.
    pub fn emit(&self, packet: &mut [u8]) {
        packet[..self.name.len()].copy_from_slice(self.name);
        let rest = &mut packet[self.name.len()..];
        NetworkEndian::write_u16(&mut rest[0..2], self.type_.into());
        NetworkEndian::write_u16(&mut rest[2..4], CLASS_IN);
    }
}

#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Record<'a> {
    pub name: &'a [u8],
    pub ttl: u32,
    pub data: RecordData<'a>,
}

impl<'a> RecordData<'a> {
    pub fn parse(type_: Type, data: &'a [u8]) -> Result<RecordData<'a>> {
        match type_ {
            #[cfg(feature = "proto-ipv4")]
            Type::A => {
                if data.len() != 4 {
                    return Err(Error);
                }
                Ok(RecordData::A(Ipv4Address::from_bytes(data)))
            }
            #[cfg(feature = "proto-ipv6")]
            Type::Aaaa => {
                if data.len() != 16 {
                    return Err(Error);
                }
                Ok(RecordData::Aaaa(Ipv6Address::from_bytes(data)))
            }
            Type::Cname => Ok(RecordData::Cname(data)),
            x => Ok(RecordData::Other(x, data)),
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum RecordData<'a> {
    #[cfg(feature = "proto-ipv4")]
    A(Ipv4Address),
    #[cfg(feature = "proto-ipv6")]
    Aaaa(Ipv6Address),
    Cname(&'a [u8]),
    Other(Type, &'a [u8]),
}

impl<'a> Record<'a> {
    pub fn parse(buffer: &'a [u8]) -> Result<(&'a [u8], Record<'a>)> {
        let (rest, _) = parse_name_part(buffer, |_| ())?;
        let name = &buffer[..buffer.len() - rest.len()];

        if rest.len() < 10 {
            return Err(Error);
        }
        let type_ = NetworkEndian::read_u16(&rest[0..2]).into();
        let class = NetworkEndian::read_u16(&rest[2..4]);
        let ttl = NetworkEndian::read_u32(&rest[4..8]);
        let len = NetworkEndian::read_u16(&rest[8..10]) as usize;
        let rest = &rest[10..];

        if class != CLASS_IN {
            return Err(Error);
        }

        let data = rest.get(..len).ok_or(Error)?;
        let rest = &rest[len..];

        Ok((
            rest,
            Record {
                name,
                ttl,
                data: RecordData::parse(type_, data)?,
            },
        ))
    }
}

/// High-level DNS packet representation.
///
/// Currently only supports query packets.
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Repr<'a> {
    pub transaction_id: u16,
    pub opcode: Opcode,
    pub flags: Flags,
    pub question: Question<'a>,
}

impl<'a> Repr<'a> {
    /// Return the length of a packet that will be emitted from this high-level representation.
    pub const fn buffer_len(&self) -> usize {
        field::HEADER_END + self.question.buffer_len()
    }

    /// Emit a high-level representation into a DNS packet.
    pub fn emit<T: ?Sized>(&self, packet: &mut Packet<&mut T>)
    where
        T: AsRef<[u8]> + AsMut<[u8]>,
    {
        packet.set_transaction_id(self.transaction_id);
        packet.set_flags(self.flags);
        packet.set_opcode(self.opcode);
        packet.set_question_count(1);
        packet.set_answer_record_count(0);
        packet.set_authority_record_count(0);
        packet.set_additional_record_count(0);
        self.question.emit(packet.payload_mut())
    }
}

#[cfg(feature = "proto-ipv4")] // tests assume ipv4
#[cfg(test)]
mod test {
    use super::*;
    use std::vec::Vec;

    #[test]
    fn test_parse_name() {
        let bytes = &[
            0x78, 0x6c, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77,
            0x77, 0x77, 0x08, 0x66, 0x61, 0x63, 0x65, 0x62, 0x6f, 0x6f, 0x6b, 0x03, 0x63, 0x6f,
            0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00,
            0x05, 0xf3, 0x00, 0x11, 0x09, 0x73, 0x74, 0x61, 0x72, 0x2d, 0x6d, 0x69, 0x6e, 0x69,
            0x04, 0x63, 0x31, 0x30, 0x72, 0xc0, 0x10, 0xc0, 0x2e, 0x00, 0x01, 0x00, 0x01, 0x00,
            0x00, 0x00, 0x05, 0x00, 0x04, 0x1f, 0x0d, 0x53, 0x24,
        ];
        let packet = Packet::new_unchecked(bytes);

        let name_vec = |bytes| {
            let mut v = Vec::new();
            packet
                .parse_name(bytes)
                .try_for_each(|label| label.map(|label| v.push(label)))
                .map(|_| v)
        };

        //assert_eq!(parse_name_len(bytes, 0x0c), Ok(18));
        assert_eq!(
            name_vec(&bytes[0x0c..]),
            Ok(vec![&b"www"[..], &b"facebook"[..], &b"com"[..]])
        );
        //assert_eq!(parse_name_len(bytes, 0x22), Ok(2));
        assert_eq!(
            name_vec(&bytes[0x22..]),
            Ok(vec![&b"www"[..], &b"facebook"[..], &b"com"[..]])
        );
        //assert_eq!(parse_name_len(bytes, 0x2e), Ok(17));
        assert_eq!(
            name_vec(&bytes[0x2e..]),
            Ok(vec![
                &b"star-mini"[..],
                &b"c10r"[..],
                &b"facebook"[..],
                &b"com"[..]
            ])
        );
        //assert_eq!(parse_name_len(bytes, 0x3f), Ok(2));
        assert_eq!(
            name_vec(&bytes[0x3f..]),
            Ok(vec![
                &b"star-mini"[..],
                &b"c10r"[..],
                &b"facebook"[..],
                &b"com"[..]
            ])
        );
    }

    struct Parsed<'a> {
        packet: Packet<&'a [u8]>,
        questions: Vec<Question<'a>>,
        answers: Vec<Record<'a>>,
        authorities: Vec<Record<'a>>,
        additionals: Vec<Record<'a>>,
    }

    impl<'a> Parsed<'a> {
        fn parse(bytes: &'a [u8]) -> Result<Self> {
            let packet = Packet::new_unchecked(bytes);
            let mut questions = Vec::new();
            let mut answers = Vec::new();
            let mut authorities = Vec::new();
            let mut additionals = Vec::new();

            let mut payload = &bytes[12..];

            for _ in 0..packet.question_count() {
                let (p, r) = Question::parse(payload)?;
                questions.push(r);
                payload = p;
            }
            for _ in 0..packet.answer_record_count() {
                let (p, r) = Record::parse(payload)?;
                answers.push(r);
                payload = p;
            }
            for _ in 0..packet.authority_record_count() {
                let (p, r) = Record::parse(payload)?;
                authorities.push(r);
                payload = p;
            }
            for _ in 0..packet.additional_record_count() {
                let (p, r) = Record::parse(payload)?;
                additionals.push(r);
                payload = p;
            }

            // Check that there are no bytes left
            assert_eq!(payload.len(), 0);

            Ok(Parsed {
                packet,
                questions,
                answers,
                authorities,
                additionals,
            })
        }
    }

    #[test]
    fn test_parse_request() {
        let p = Parsed::parse(&[
            0x51, 0x84, 0x01, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x67,
            0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
        ])
        .unwrap();

        assert_eq!(p.packet.transaction_id(), 0x5184);
        assert_eq!(
            p.packet.flags(),
            Flags::RECURSION_DESIRED | Flags::AUTHENTIC_DATA
        );
        assert_eq!(p.packet.opcode(), Opcode::Query);
        assert_eq!(p.packet.question_count(), 1);
        assert_eq!(p.packet.answer_record_count(), 0);
        assert_eq!(p.packet.authority_record_count(), 0);
        assert_eq!(p.packet.additional_record_count(), 0);

        assert_eq!(p.questions.len(), 1);
        assert_eq!(
            p.questions[0].name,
            &[0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00]
        );
        assert_eq!(p.questions[0].type_, Type::A);

        assert_eq!(p.answers.len(), 0);
        assert_eq!(p.authorities.len(), 0);
        assert_eq!(p.additionals.len(), 0);
    }

    #[test]
    fn test_parse_response() {
        let p = Parsed::parse(&[
            0x51, 0x84, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x67,
            0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
            0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xca, 0x00, 0x04, 0xac, 0xd9,
            0xa8, 0xae,
        ])
        .unwrap();

        assert_eq!(p.packet.transaction_id(), 0x5184);
        assert_eq!(
            p.packet.flags(),
            Flags::RESPONSE | Flags::RECURSION_DESIRED | Flags::RECURSION_AVAILABLE
        );
        assert_eq!(p.packet.opcode(), Opcode::Query);
        assert_eq!(p.packet.rcode(), Rcode::NoError);
        assert_eq!(p.packet.question_count(), 1);
        assert_eq!(p.packet.answer_record_count(), 1);
        assert_eq!(p.packet.authority_record_count(), 0);
        assert_eq!(p.packet.additional_record_count(), 0);

        assert_eq!(
            p.questions[0].name,
            &[0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00]
        );
        assert_eq!(p.questions[0].type_, Type::A);

        assert_eq!(p.answers[0].name, &[0xc0, 0x0c]);
        assert_eq!(p.answers[0].ttl, 202);
        assert_eq!(
            p.answers[0].data,
            RecordData::A(Ipv4Address::new(0xac, 0xd9, 0xa8, 0xae))
        );
    }

    #[test]
    fn test_parse_response_multiple_a() {
        let p = Parsed::parse(&[
            0x4b, 0x9e, 0x81, 0x80, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x72,
            0x75, 0x73, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00,
            0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00,
            0x04, 0x0d, 0xe0, 0x77, 0x35, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
            0x09, 0x00, 0x04, 0x0d, 0xe0, 0x77, 0x28, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00,
            0x00, 0x00, 0x09, 0x00, 0x04, 0x0d, 0xe0, 0x77, 0x43, 0xc0, 0x0c, 0x00, 0x01, 0x00,
            0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x04, 0x0d, 0xe0, 0x77, 0x62,
        ])
        .unwrap();

        assert_eq!(p.packet.transaction_id(), 0x4b9e);
        assert_eq!(
            p.packet.flags(),
            Flags::RESPONSE | Flags::RECURSION_DESIRED | Flags::RECURSION_AVAILABLE
        );
        assert_eq!(p.packet.opcode(), Opcode::Query);
        assert_eq!(p.packet.rcode(), Rcode::NoError);
        assert_eq!(p.packet.question_count(), 1);
        assert_eq!(p.packet.answer_record_count(), 4);
        assert_eq!(p.packet.authority_record_count(), 0);
        assert_eq!(p.packet.additional_record_count(), 0);

        assert_eq!(
            p.questions[0].name,
            &[
                0x09, 0x72, 0x75, 0x73, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x03, 0x6f, 0x72, 0x67,
                0x00
            ]
        );
        assert_eq!(p.questions[0].type_, Type::A);

        assert_eq!(p.answers[0].name, &[0xc0, 0x0c]);
        assert_eq!(p.answers[0].ttl, 9);
        assert_eq!(
            p.answers[0].data,
            RecordData::A(Ipv4Address::new(0x0d, 0xe0, 0x77, 0x35))
        );

        assert_eq!(p.answers[1].name, &[0xc0, 0x0c]);
        assert_eq!(p.answers[1].ttl, 9);
        assert_eq!(
            p.answers[1].data,
            RecordData::A(Ipv4Address::new(0x0d, 0xe0, 0x77, 0x28))
        );

        assert_eq!(p.answers[2].name, &[0xc0, 0x0c]);
        assert_eq!(p.answers[2].ttl, 9);
        assert_eq!(
            p.answers[2].data,
            RecordData::A(Ipv4Address::new(0x0d, 0xe0, 0x77, 0x43))
        );

        assert_eq!(p.answers[3].name, &[0xc0, 0x0c]);
        assert_eq!(p.answers[3].ttl, 9);
        assert_eq!(
            p.answers[3].data,
            RecordData::A(Ipv4Address::new(0x0d, 0xe0, 0x77, 0x62))
        );
    }

    #[test]
    fn test_parse_response_cname() {
        let p = Parsed::parse(&[
            0x78, 0x6c, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77,
            0x77, 0x77, 0x08, 0x66, 0x61, 0x63, 0x65, 0x62, 0x6f, 0x6f, 0x6b, 0x03, 0x63, 0x6f,
            0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00,
            0x05, 0xf3, 0x00, 0x11, 0x09, 0x73, 0x74, 0x61, 0x72, 0x2d, 0x6d, 0x69, 0x6e, 0x69,
            0x04, 0x63, 0x31, 0x30, 0x72, 0xc0, 0x10, 0xc0, 0x2e, 0x00, 0x01, 0x00, 0x01, 0x00,
            0x00, 0x00, 0x05, 0x00, 0x04, 0x1f, 0x0d, 0x53, 0x24,
        ])
        .unwrap();

        assert_eq!(p.packet.transaction_id(), 0x786c);
        assert_eq!(
            p.packet.flags(),
            Flags::RESPONSE | Flags::RECURSION_DESIRED | Flags::RECURSION_AVAILABLE
        );
        assert_eq!(p.packet.opcode(), Opcode::Query);
        assert_eq!(p.packet.rcode(), Rcode::NoError);
        assert_eq!(p.packet.question_count(), 1);
        assert_eq!(p.packet.answer_record_count(), 2);
        assert_eq!(p.packet.authority_record_count(), 0);
        assert_eq!(p.packet.additional_record_count(), 0);

        assert_eq!(
            p.questions[0].name,
            &[
                0x03, 0x77, 0x77, 0x77, 0x08, 0x66, 0x61, 0x63, 0x65, 0x62, 0x6f, 0x6f, 0x6b, 0x03,
                0x63, 0x6f, 0x6d, 0x00
            ]
        );
        assert_eq!(p.questions[0].type_, Type::A);

        // cname
        assert_eq!(p.answers[0].name, &[0xc0, 0x0c]);
        assert_eq!(p.answers[0].ttl, 1523);
        assert_eq!(
            p.answers[0].data,
            RecordData::Cname(&[
                0x09, 0x73, 0x74, 0x61, 0x72, 0x2d, 0x6d, 0x69, 0x6e, 0x69, 0x04, 0x63, 0x31, 0x30,
                0x72, 0xc0, 0x10
            ])
        );
        // a
        assert_eq!(p.answers[1].name, &[0xc0, 0x2e]);
        assert_eq!(p.answers[1].ttl, 5);
        assert_eq!(
            p.answers[1].data,
            RecordData::A(Ipv4Address::new(0x1f, 0x0d, 0x53, 0x24))
        );
    }

    #[test]
    fn test_parse_response_nxdomain() {
        let p = Parsed::parse(&[
            0x63, 0xc4, 0x81, 0x83, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x13, 0x61,
            0x68, 0x61, 0x73, 0x64, 0x67, 0x68, 0x6c, 0x61, 0x6b, 0x73, 0x6a, 0x68, 0x62, 0x61,
            0x61, 0x73, 0x6c, 0x64, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0,
            0x20, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x03, 0x83, 0x00, 0x3d, 0x01, 0x61, 0x0c,
            0x67, 0x74, 0x6c, 0x64, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x03, 0x6e,
            0x65, 0x74, 0x00, 0x05, 0x6e, 0x73, 0x74, 0x6c, 0x64, 0x0c, 0x76, 0x65, 0x72, 0x69,
            0x73, 0x69, 0x67, 0x6e, 0x2d, 0x67, 0x72, 0x73, 0xc0, 0x20, 0x5f, 0xce, 0x8b, 0x85,
            0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x03, 0x84, 0x00, 0x09, 0x3a, 0x80, 0x00, 0x01,
            0x51, 0x80,
        ])
        .unwrap();

        assert_eq!(p.packet.transaction_id(), 0x63c4);
        assert_eq!(
            p.packet.flags(),
            Flags::RESPONSE | Flags::RECURSION_DESIRED | Flags::RECURSION_AVAILABLE
        );
        assert_eq!(p.packet.opcode(), Opcode::Query);
        assert_eq!(p.packet.rcode(), Rcode::NXDomain);
        assert_eq!(p.packet.question_count(), 1);
        assert_eq!(p.packet.answer_record_count(), 0);
        assert_eq!(p.packet.authority_record_count(), 1);
        assert_eq!(p.packet.additional_record_count(), 0);

        assert_eq!(p.questions[0].type_, Type::A);

        // SOA authority
        assert_eq!(p.authorities[0].name, &[0xc0, 0x20]); // com.
        assert_eq!(p.authorities[0].ttl, 899);
        assert!(matches!(
            p.authorities[0].data,
            RecordData::Other(Type::Soa, _)
        ));
    }

    #[test]
    fn test_emit() {
        let name = &[
            0x09, 0x72, 0x75, 0x73, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x03, 0x6f, 0x72, 0x67,
            0x00,
        ];

        let repr = Repr {
            transaction_id: 0x1234,
            flags: Flags::RECURSION_DESIRED,
            opcode: Opcode::Query,
            question: Question {
                name,
                type_: Type::A,
            },
        };

        let mut buf = Vec::new();
        buf.resize(repr.buffer_len(), 0);
        repr.emit(&mut Packet::new_unchecked(&mut buf));

        let want = &[
            0x12, 0x34, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x72,
            0x75, 0x73, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00,
            0x01, 0x00, 0x01,
        ];
        assert_eq!(&buf, want);
    }
}
