// DNS message header (RFC 1035 section 4.1.1).

///|
pub struct Header {
  id : UInt16
  flags : UInt16
  qdcount : UInt16
  ancount : UInt16
  nscount : UInt16
  arcount : UInt16
}

///|
pub let flag_qr : UInt16 = 0x8000

///|
pub let flag_tc : UInt16 = 0x0200

///|
pub let flag_rd : UInt16 = 0x0100

///|
pub let flag_ra : UInt16 = 0x0080

///|
pub let flag_aa : UInt16 = 0x0400

///|
pub fn Header::encode(self : Header) -> Array[Byte] {
  let buf = Array::make(dns_header_size, (0).to_byte())
  ignore(wire_put_u16(buf, 0, self.id))
  ignore(wire_put_u16(buf, 2, self.flags))
  ignore(wire_put_u16(buf, 4, self.qdcount))
  ignore(wire_put_u16(buf, 6, self.ancount))
  ignore(wire_put_u16(buf, 8, self.nscount))
  ignore(wire_put_u16(buf, 10, self.arcount))
  buf
}

///|
pub fn decode_header(
  bytes : Array[Byte],
  offset? : Int = 0,
) -> Result[(Header, Int), String] {
  match wire_check_range(bytes, offset, dns_header_size) {
    Err(err) => Err("DNS header: " + err)
    Ok(_) => {
      let (id, o1) = match wire_get_u16(bytes, offset) {
        Ok(value) => value
        Err(err) => return Err("DNS header: " + err)
      }
      let (flags, o2) = match wire_get_u16(bytes, o1) {
        Ok(value) => value
        Err(err) => return Err("DNS header: " + err)
      }
      let (qdcount, o3) = match wire_get_u16(bytes, o2) {
        Ok(value) => value
        Err(err) => return Err("DNS header: " + err)
      }
      let (ancount, o4) = match wire_get_u16(bytes, o3) {
        Ok(value) => value
        Err(err) => return Err("DNS header: " + err)
      }
      let (nscount, o5) = match wire_get_u16(bytes, o4) {
        Ok(value) => value
        Err(err) => return Err("DNS header: " + err)
      }
      let (arcount, o6) = match wire_get_u16(bytes, o5) {
        Ok(value) => value
        Err(err) => return Err("DNS header: " + err)
      }
      Ok(({ id, flags, qdcount, ancount, nscount, arcount }, o6))
    }
  }
}

///|
fn Header::is_response(self : Header) -> Bool {
  (self.flags & flag_qr) != 0
}

///|
fn Header::is_truncated(self : Header) -> Bool {
  (self.flags & flag_tc) != 0
}

///|
fn Header::rcode(self : Header) -> Int {
  (self.flags & mask_rcode).to_int()
}

///|
fn Header::opcode(self : Header) -> UInt16 {
  (self.flags & mask_opcode) >> 11
}