// MoonDNS Core Types (RFC 1035)

///|
/// DNS Resource Record Types
pub(all) enum RType {
  A
  NS
  CNAME
  SOA
  MX
  TXT
  AAAA
  OPT
  Unknown(UInt16)
} derive(Debug, Eq, Compare)

///|
pub impl Show for RType with output(self, logger) {
  let s = match self {
    A => "A"
    NS => "NS"
    CNAME => "CNAME"
    SOA => "SOA"
    MX => "MX"
    TXT => "TXT"
    AAAA => "AAAA"
    OPT => "OPT"
    Unknown(val) => "TYPE\{val.to_int()}"
  }
  logger.write_string(s)
}

///|
pub fn RType::to_uint16(self : RType) -> UInt16 {
  match self {
    A => Int::to_uint16(1)
    NS => Int::to_uint16(2)
    CNAME => Int::to_uint16(5)
    SOA => Int::to_uint16(6)
    MX => Int::to_uint16(15)
    TXT => Int::to_uint16(16)
    AAAA => Int::to_uint16(28)
    OPT => Int::to_uint16(41)
    Unknown(val) => val
  }
}

///|
pub fn RType::from_uint16(val : UInt16) -> RType {
  let v = val.to_int()
  match v {
    1 => A
    2 => NS
    5 => CNAME
    6 => SOA
    15 => MX
    16 => TXT
    28 => AAAA
    41 => OPT
    _ => Unknown(val)
  }
}

///|
/// DNS Resource Record Classes
pub(all) enum RClass {
  IN
  CS
  CH
  HS
  ANY
  Unknown(UInt16)
} derive(Debug, Eq, Compare)

///|
pub impl Show for RClass with output(self, logger) {
  let s = match self {
    IN => "IN"
    CS => "CS"
    CH => "CH"
    HS => "HS"
    ANY => "ANY"
    Unknown(val) => "CLASS\{val.to_int()}"
  }
  logger.write_string(s)
}

///|
pub fn RClass::to_uint16(self : RClass) -> UInt16 {
  match self {
    IN => Int::to_uint16(1)
    CS => Int::to_uint16(2)
    CH => Int::to_uint16(3)
    HS => Int::to_uint16(4)
    ANY => Int::to_uint16(255)
    Unknown(val) => val
  }
}

///|
pub fn RClass::from_uint16(val : UInt16) -> RClass {
  let v = val.to_int()
  match v {
    1 => IN
    2 => CS
    3 => CH
    4 => HS
    255 => ANY
    _ => Unknown(val)
  }
}

///|
/// DNS Header (12 bytes, RFC 1035 Section 4.1.1)
pub(all) struct Header {
  id : UInt16
  qr : Bool
  opcode : Int
  aa : Bool
  tc : Bool
  rd : Bool
  ra : Bool
  z : Int
  rcode : Int
  qdcount : UInt16
  ancount : UInt16
  nscount : UInt16
  arcount : UInt16
} derive(Debug, Eq)

///|
pub fn Header::new(
  id : UInt16,
  qr : Bool,
  opcode : Int,
  aa : Bool,
  tc : Bool,
  rd : Bool,
  ra : Bool,
  z : Int,
  rcode : Int,
  qdcount : UInt16,
  ancount : UInt16,
  nscount : UInt16,
  arcount : UInt16,
) -> Header {
  {
    id,
    qr,
    opcode,
    aa,
    tc,
    rd,
    ra,
    z,
    rcode,
    qdcount,
    ancount,
    nscount,
    arcount,
  }
}

///|
/// DNS Question (RFC 1035 Section 4.1.2)
pub(all) struct Question {
  name : String
  qtype : RType
  qclass : RClass
} derive(Debug, Eq)

///|
pub fn Question::new(name : String, qtype : RType, qclass : RClass) -> Question {
  { name, qtype, qclass }
}

///|
/// DNS Resource Record Data
pub(all) enum RData {
  A(UInt)
  AAAA(FixedArray[Byte])
  CNAME(String)
  NS(String)
  MX(UInt16, String)
  TXT(Array[String])
  SOA(
    mname~ : String,
    rname~ : String,
    serial~ : UInt,
    refresh~ : UInt,
    retry~ : UInt,
    expire~ : UInt,
    minimum~ : UInt,
  )
  OPT(FixedArray[Byte])
  Raw(FixedArray[Byte])
} derive(Debug, Eq)

///|
/// DNS Resource Record (RFC 1035 Section 4.1.3)
pub(all) struct Record {
  name : String
  rtype : RType
  rclass : RClass
  ttl : UInt
  rdata : RData
} derive(Debug, Eq)

///|
pub fn Record::new(
  name : String,
  rtype : RType,
  rclass : RClass,
  ttl : UInt,
  rdata : RData,
) -> Record {
  { name, rtype, rclass, ttl, rdata }
}

///|
/// Complete DNS Message
pub(all) struct Message {
  header : Header
  questions : Array[Question]
  answers : Array[Record]
  authorities : Array[Record]
  additionals : Array[Record]
} derive(Debug, Eq)

///|
pub fn Message::new(
  header : Header,
  questions : Array[Question],
  answers : Array[Record],
  authorities : Array[Record],
  additionals : Array[Record],
) -> Message {
  { header, questions, answers, authorities, additionals }
}

///|
/// DNS Parsing and Protocol Errors
pub(all) enum DnsError {
  BufferUnderflow
  MalformedPacket(String)
  PointerLoop(String)
  PointerOutOfBounds(String)
  PointerJumpLimitExceeded
  InvalidLabelLength(Int)
  InvalidDomainName(String)
  UnexpectedEof
  Other(String)
} derive(Debug, Eq)