use std::net::{Ipv4Addr, SocketAddr};
use crate::message::{DNSQuestion, DNSHeader, questions_to_bytes, Direction, ResponseCode, QType, QClass, ResourceRecord, records_to_bytes, ARdata, TXTRdata, RData};
use crate::RequestError;
pub const MESSAGE_SIZE: usize = 512;
#[derive(Debug)]
pub struct DNSMessage {
    pub header: DNSHeader,
    pub questions: Vec<DNSQuestion>,
    pub answer_records: Vec<ResourceRecord>,
    pub authority_records: Vec<ResourceRecord>,
    pub additional_records: Vec<ResourceRecord>,
    pub peer: SocketAddr
}
impl DNSMessage {
    pub fn to_bytes(& self) -> Vec<u8>
    {
        let mut header_bytes = self.header.to_bytes().to_vec();
        let mut body_bytes = questions_to_bytes(&self.questions);
        let mut answer_bytes = records_to_bytes(&self.answer_records);
        let mut authority_bytes = records_to_bytes(&self.authority_records);
        let mut additional_bytes = records_to_bytes(&self.additional_records);
        header_bytes.append(&mut body_bytes);
        header_bytes.append(&mut answer_bytes);
        header_bytes.append(&mut authority_bytes);
        header_bytes.append(&mut additional_bytes);
        return header_bytes
    }
    pub fn req_from_hostname(peer: SocketAddr, id: u16, hostname: String) -> DNSMessage
    {
        DNSMessage {
            header: DNSHeader::new_request(id, None),
            questions: vec![
                DNSQuestion {
                    qname: hostname,
                    qtype: QType::A,
                    qclass: QClass::Internet
                }
            ],
            answer_records: vec![],
            authority_records: vec![],
            additional_records: vec![],
            peer
        }
    }
    pub fn reqs_from_hostnames(peer: SocketAddr, id: u16, hostnames: Vec<String>) -> DNSMessage
    {
        DNSMessage {
            header: DNSHeader::new_request(id, Some(hostnames.len() as u16)),
            questions: hostnames
                .into_iter()
                .map(|n|
                    DNSQuestion {
                        qname: n,
                        qclass: QClass::Internet,
                        qtype: QType::A
                    })
                .collect(),
            answer_records: vec![],
            authority_records: vec![],
            additional_records: vec![],
            peer
        }
    }
    pub fn a_resp_from_request(&self, ip: impl Fn(&DNSQuestion) -> Ipv4Addr) -> DNSMessage
    {
        let mut response = DNSMessage{
            header: self.header.clone(),
            questions: self.questions.clone(),
            answer_records: vec![],
            authority_records: vec![],
            additional_records: vec![],
            peer: self.peer
        };
        response.answer_records = self.questions
            .iter()
            .map(|x|
                ResourceRecord::from_query(x,
                                           12,
                                           Box::new(ARdata::from(ip(x))),
                                           None))
            .collect();
        response.header.direction = Direction::Response;
        response.header.response = ResponseCode::NoError;
        response.header.answer_record_count = response.answer_records.len() as u16;
        response.header.authority_record_count = 0;
        response.header.additional_record_count = 0;
        if response.header.recursion_desired {
            response.header.recursion_available = true;
        }
        response
    }
    pub fn dumb_resp_from_request(&self) -> DNSMessage
    {
        let mut response = DNSMessage{
            header: self.header.clone(),
            questions: self.questions.clone(),
            answer_records: vec![],
            authority_records: vec![],
            additional_records: vec![],
            peer: self.peer
        };
        response.header.direction = Direction::Response;
        response.header.response = ResponseCode::NotImplemented;
        response.header.answer_record_count = 0;
        response.header.authority_record_count = 0;
        response.header.additional_record_count = 0;
        response
    }
    pub fn empty_resp_from_request(&self) -> DNSMessage
    {
        let mut response = DNSMessage{
            header: self.header.clone(),
            questions: self.questions.clone(),
            answer_records: vec![],
            authority_records: vec![],
            additional_records: vec![],
            peer: self.peer
        };
        response.header.direction = Direction::Response;
        response.header.response = ResponseCode::NoError;
        response.header.answer_record_count = 0;
        response.header.authority_record_count = 0;
        response.header.additional_record_count = 0;
        if response.header.recursion_desired {
            response.header.recursion_available = true;
        }
        response
    }
    pub fn protocol_error_from_request(&self, _error_code: RequestError) -> DNSMessage
    {
        let txt = Box::new(TXTRdata::from(String::new()));
        let mut response = DNSMessage{
            header: self.header.clone(),
            questions: self.questions.clone(),
            answer_records: vec![ResourceRecord {
                name_offset: 12,
                answer_type: QType::TXT,
                class: QClass::Internet,
                ttl: 0,
                rd_length: txt.to_bytes().len() as u16,
                r_data: txt
            }],
            authority_records: vec![],
            additional_records: vec![],
            peer: self.peer
        };
        response.header.direction = Direction::Response;
        response.header.response = ResponseCode::ServerFailure;
        response.header.answer_record_count = 1;
        response.header.authority_record_count = 0;
        response.header.additional_record_count = 0;
        response
    }
}