use std::net::Ipv4Addr;

use dns::{Response, Query, Answer, Labels, Flags, Opcode, QClass, qtype};
use dns::record::{Record, A, CNAME, OPT};


#[test]
fn parse_nothing() {
    assert!(Response::from_bytes(&[]).is_err());
}


#[test]
fn parse_response_standard() {
    let buf = &[
        0x0d, 0xcd,  // transaction ID
        0x81, 0x80,  // flags (standard query, response, no error)
        0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,  // counts (1, 1, 0, 1)

        // the query:
        0x03, 0x64, 0x6e, 0x73, 0x06, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x03,
        0x64, 0x6f, 0x67, 0x00,  // "dns.lookup.dog."
        0x00, 0x01,  // type A
        0x00, 0x01,  // class IN

        // the answer:
        0xc0, 0x0c,  // to find the name, backtrack to position 0x0c (12)
        0x00, 0x01,  // type A
        0x00, 0x01,  // class IN
        0x00, 0x00, 0x03, 0xa5,  // TTL (933 seconds)
        0x00, 0x04,  // record data length 4
        0x8a, 0x44, 0x75, 0x5e,  // record date (138.68.117.94)

        // the additional:
        0x00,        // no name
        0x00, 0x29,  // type OPT
        0x02, 0x00,  // UDP payload size (512)
        0x00, 0x00,  // higher bits (all 0)
        0x00,        // EDNS version
        0x00, 0x00,  // extra bits (DO bit unset)
        0x00,        // data length 0
    ];

    let response = Response {
        transaction_id: 0x0dcd,
        flags: Flags {
            response: true,
            opcode: Opcode::Query,
            authoritative: false,
            truncated: false,
            recursion_desired: true,
            recursion_available: true,
            authentic_data: false,
            checking_disabled: false,
            error_code: None,
        },
        queries: vec![
            Query {
                qname: Labels::encode("dns.lookup.dog").unwrap(),
                qclass: QClass::IN,
                qtype: qtype!(A),
            },
        ],
        answers: vec![
            Answer::Standard {
                qname: Labels::encode("dns.lookup.dog").unwrap(),
                qclass: QClass::IN,
                ttl: 933,
                record: Record::A(A {
                    address: Ipv4Addr::new(138, 68, 117, 94),
                }),
            }
        ],
        authorities: vec![],
        additionals: vec![
            Answer::Pseudo {
                qname: Labels::root(),
                opt: OPT {
                    udp_payload_size: 512,
                    higher_bits: 0,
                    edns0_version: 0,
                    flags: 0,
                    data: vec![],
                },
            },
        ],
    };

    assert_eq!(Response::from_bytes(buf), Ok(response));
}


#[test]
fn parse_response_with_mixed_string() {
    let buf = &[
        0x06, 0x9f,  // transaction ID
        0x81, 0x80,  // flags (standard query, response, no error)
        0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,  // counts (1, 1, 0, 0)

        // the query:
        0x0d, 0x63, 0x6e, 0x61, 0x6d, 0x65, 0x2d, 0x65, 0x78, 0x61, 0x6d, 0x70,
        0x6c, 0x65, 0x06, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x03, 0x64, 0x6f,
        0x67, 0x00,  // "cname-example.lookup.dog"
        0x00, 0x05,  // type CNAME
        0x00, 0x01,  // class IN

        // the answer:
        0xc0, 0x0c,  // to find the name, backtrack to position 0x0c (12)
        0x00, 0x05,  // type CNAME
        0x00, 0x01,  // class IN
        0x00, 0x00, 0x03, 0x69,  // TTL (873 seconds)
        0x00, 0x06,  // record data length 6
        0x03, 0x64, 0x6e, 0x73, 0xc0, 0x1a,
        // "dns.lookup.dog.", which is "dns." + backtrack to position 0x1a (28)
    ];

    let response = Response {
        transaction_id: 0x069f,
        flags: Flags {
            response: true,
            opcode: Opcode::Query,
            authoritative: false,
            truncated: false,
            recursion_desired: true,
            recursion_available: true,
            authentic_data: false,
            checking_disabled: false,
            error_code: None,
        },
        queries: vec![
            Query {
                qname: Labels::encode("cname-example.lookup.dog").unwrap(),
                qclass: QClass::IN,
                qtype: qtype!(CNAME),
            },
        ],
        answers: vec![
            Answer::Standard {
                qname: Labels::encode("cname-example.lookup.dog").unwrap(),
                qclass: QClass::IN,
                ttl: 873,
                record: Record::CNAME(CNAME {
                    domain: Labels::encode("dns.lookup.dog").unwrap(),
                }),
            }
        ],
        authorities: vec![],
        additionals: vec![],
    };

    assert_eq!(Response::from_bytes(buf), Ok(response));
}