mod raw_rdata;
pub use raw_rdata::RawRData;
mod a_rdata;
pub use a_rdata::ARdata;
mod aaaa_rdata;
pub use aaaa_rdata::AAAARdata;
mod txt_rdata;
pub use txt_rdata::TXTRdata;
mod cname_rdata;
pub use cname_rdata::CnameRdata;
#[cfg(test)]
mod tests;
use std::fmt::Debug;
use crate::byte::{four_byte_combine, four_byte_split, push_split_bytes, two_byte_combine};
use crate::message::question::{DNSQuestion, QClass, QType};
pub trait RData: Debug {
    fn to_bytes(&self) -> Vec<u8>;
}
#[derive(Debug)]
pub struct ResourceRecord {
    pub name_offset: u16,
    pub answer_type: QType,
    pub class: QClass,
    pub ttl: u32,
    pub rd_length: u16,
    pub r_data: Box<dyn RData>
}
impl ResourceRecord {
    pub fn to_bytes(&self) -> Vec<u8>
    {
        let mut data_bytes = self.r_data.to_bytes();
        let mut ret = Vec::with_capacity(2 + 2 + 2 + 4 + 2 + data_bytes.len());
        push_split_bytes(&mut ret, self.name_offset | (0b11 << 14));
        push_split_bytes(&mut ret, self.answer_type as u16);
        push_split_bytes(&mut ret, self.class as u16);
        let (ttl_1, ttl_2, ttl_3, ttl_4) = four_byte_split(self.ttl);
        ret.push(ttl_1);
        ret.push(ttl_2);
        ret.push(ttl_3);
        ret.push(ttl_4);
        push_split_bytes(&mut ret, self.rd_length);
        ret.append(&mut data_bytes);
        return ret
    }
    pub fn from_query(query: &DNSQuestion, name_offset: u16, data: Box<dyn RData>, ttl: Option<u32>) -> ResourceRecord
    {
        ResourceRecord {
            name_offset,
            answer_type: query.qtype,
            class: query.qclass,
            ttl: ttl.unwrap_or(0),
            rd_length: data.to_bytes().len() as u16,
            r_data: data
        }
    }
}
pub fn records_to_bytes(answers: &Vec<ResourceRecord>) -> Vec<u8>
{
    let mut ret = Vec::with_capacity(20);
    for a in answers
    {
        ret.append(&mut a.to_bytes());
    }
    ret
}
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug)]
pub enum RecordParseError {
    ShortLength(usize),
    QTypeParse(u16),
    QClassParse(u16)
}
pub fn records_from_bytes(bytes: Vec<u8>, total_answers: u16) -> Result<(Vec<ResourceRecord>, Vec<u8>), RecordParseError>
{
    let mut ret = Vec::with_capacity(total_answers as usize);
    let mut remaining = vec![];
    let mut name_offset = (None, None);
    let mut qtype = (None, None);
    let mut qclass = (None, None);
    let mut ttl = (None, None, None, None);
    let mut data_length = (None, None);
    let mut data_length_combined = None;
    let mut data_length_remaining: u16 = 0;
    let mut data = Vec::with_capacity(8);
    for byte in bytes {
        if ret.len() == total_answers as usize {
            remaining.push(byte);
            continue;
        }
        if data_length_remaining != 0 || data_length_combined == Some(0) {
            if data_length_remaining != 0 {
                data.push(byte);
                data_length_remaining -= 1;
            }
            if data_length_remaining == 0 {
                match (two_byte_combine(qtype.0.unwrap(), qtype.1.unwrap()).try_into(),
                       two_byte_combine(qclass.0.unwrap(), qclass.1.unwrap()).try_into())
                {
                    (Ok(qtype_formed), Ok(qclass_formed)) => {
                        ret.push(ResourceRecord {
                            name_offset: two_byte_combine(name_offset.0.unwrap(), name_offset.1.unwrap()) & 0b11111111111111,
                            answer_type: qtype_formed,
                            class: qclass_formed,
                            ttl: four_byte_combine(ttl.0.unwrap(), ttl.1.unwrap(), ttl.2.unwrap(), ttl.3.unwrap()),
                            rd_length: data_length_combined.unwrap(),
                            r_data: Box::new(RawRData::from(data.clone()))
                        });
                        name_offset = (None, None);
                        qtype = (None, None);
                        qclass = (None, None);
                        ttl = (None, None, None, None);
                        data_length = (None, None);
                        data_length_combined = None;
                        data.clear();
                    }
                    (Err(qtype_e), _) => {
                        return Err(RecordParseError::QTypeParse(qtype_e));
                    }
                    (_, Err(qclass_e)) => {
                        return Err(RecordParseError::QClassParse(qclass_e));
                    }
                }
            }
        }
        else {
            match (name_offset, qtype, qclass, ttl, data_length) {
                ((None, _), _, _, _, _) => {
                    name_offset.0 = Some(byte);
                }
                ((Some(_), None), _, _, _, _) => {
                    name_offset.1 = Some(byte);
                }
                ((Some(_), Some(_)), (None, _),
                    _, _, _) => {
                    qtype.0 = Some(byte);
                }
                ((Some(_), Some(_)), (Some(_), None),
                    _, _, _) => {
                    qtype.1 = Some(byte);
                }
                ((Some(_), Some(_)), (Some(_), Some(_)),
                    (None, _),
                    _, _) => {
                    qclass.0 = Some(byte);
                }
                ((Some(_), Some(_)), (Some(_), Some(_)),
                    (Some(_), None),
                    _, _) => {
                    qclass.1 = Some(byte);
                }
                ((Some(_), Some(_)), (Some(_), Some(_)),
                    (Some(_), Some(_)),
                    (None, _, _, _),
                    _) => {
                    ttl.0 = Some(byte);
                }
                ((Some(_), Some(_)), (Some(_), Some(_)),
                    (Some(_), Some(_)),
                    (Some(_), None, _, _),
                    _) => {
                    ttl.1 = Some(byte);
                }
                ((Some(_), Some(_)), (Some(_), Some(_)),
                    (Some(_), Some(_)),
                    (Some(_), Some(_), None, _),
                    _) => {
                    ttl.2 = Some(byte);
                }
                ((Some(_), Some(_)), (Some(_), Some(_)),
                    (Some(_), Some(_)),
                    (Some(_), Some(_), Some(_), None),
                    _) => {
                    ttl.3 = Some(byte);
                }
                ((Some(_), Some(_)), (Some(_), Some(_)),
                    (Some(_), Some(_)),
                    (Some(_), Some(_), Some(_), Some(_)),
                    (None, _)) => {
                    data_length.0 = Some(byte);
                }
                ((Some(_), Some(_)), (Some(_), Some(_)),
                    (Some(_), Some(_)),
                    (Some(_), Some(_), Some(_), Some(_)),
                    (Some(data_length_1), None)) => {
                    data_length.1 = Some(byte);
                    data_length_combined = Some(two_byte_combine(data_length_1, byte));
                    data_length_remaining = data_length_combined.unwrap();
                }
                ((Some(_), Some(_)),
                    (Some(_), Some(_)),
                    (Some(_), Some(_)),
                    (Some(_), Some(_), Some(_), Some(_)),
                    (Some(_), Some(_))) => {
                }
            }
        }
    }
    Ok((ret, remaining))
}