///|
pub const TLV_ALPN : Byte = b'\x01'

///|
pub const TLV_AUTHORITY : Byte = b'\x02'

///|
pub const TLV_CRC32C : Byte = b'\x03'

///|
pub const TLV_NOOP : Byte = b'\x04'

///|
pub const TLV_UNIQUE_ID : Byte = b'\x05'

///|
pub const TLV_SSL : Byte = b'\x20'

///|
pub const TLV_SSL_VERSION : Byte = b'\x21'

///|
pub const TLV_SSL_CN : Byte = b'\x22'

///|
pub const TLV_SSL_CIPHER : Byte = b'\x23'

///|
pub const TLV_SSL_SIG_ALG : Byte = b'\x24'

///|
pub const TLV_SSL_KEY_ALG : Byte = b'\x25'

///|
pub const TLV_SSL_GROUP : Byte = b'\x26'

///|
pub const TLV_SSL_SIG_SCHEME : Byte = b'\x27'

///|
pub const TLV_SSL_CLIENT_CERT : Byte = b'\x28'

///|
pub const TLV_NETNS : Byte = b'\x30'

///|
fn is_registered_tlv(kind : Byte) -> Bool {
  let n = kind.to_int()
  n == 1 ||
  n == 2 ||
  n == 3 ||
  n == 4 ||
  n == 5 ||
  (n >= 0x20 && n <= 0x28) ||
  n == 0x30
}

///|
pub fn parse_tlvs(
  input : Bytes,
  offset? : Int = 0,
  preserve_unknown? : Bool = true,
) -> Result[Array[RawTlv], ProxyError] {
  let result = Array::make(0, { type_code: b'\x00', value: Bytes::new(0) })
  let mut at = 0
  let mut saw_crc = false
  while at < input.length() {
    if input.length() - at < 3 {
      return Err(
        proxy_error(TruncatedTlv, offset + at, "incomplete TLV header"),
      )
    }
    let kind = input[at]
    let length = (input[at + 1].to_int() << 8) | input[at + 2].to_int()
    if length > input.length() - at - 3 {
      return Err(
        proxy_error(
          TruncatedTlv,
          offset + at,
          "declared TLV value exceeds header",
        ),
      )
    }
    let value = Bytes::from_array(input.to_array()[at + 3:at + 3 + length])
    if kind == TLV_CRC32C {
      if length != 4 {
        return Err(
          proxy_error(InvalidTlv, offset + at, "CRC32C TLV must be four bytes"),
        )
      }
      if saw_crc {
        return Err(
          proxy_error(DuplicateCrc32c, offset + at, "more than one CRC32C TLV"),
        )
      }
      saw_crc = true
    }
    if is_registered_tlv(kind) || preserve_unknown {
      result.push({ type_code: kind, value })
    }
    at = at + 3 + length
  }
  Ok(result)
}

///|
pub fn encode_tlvs(tlvs : Array[RawTlv]) -> Result[Bytes, ProxyError] {
  let buffer = @buffer.Buffer(size_hint=32)
  let mut saw_crc = false
  for tlv in tlvs {
    if tlv.value.length() > 65535 {
      return Err(
        proxy_error(InvalidLength, 0, "TLV value is longer than 65535 bytes"),
      )
    }
    if tlv.type_code == TLV_CRC32C {
      if saw_crc {
        return Err(proxy_error(DuplicateCrc32c, 0, "more than one CRC32C TLV"))
      }
      if tlv.value.length() != 4 {
        return Err(proxy_error(InvalidTlv, 0, "CRC32C TLV must be four bytes"))
      }
      saw_crc = true
    }
    buffer.write_byte(tlv.type_code)
    buffer.write_bytes(write_u16_be(tlv.value.length()))
    buffer.write_bytes(tlv.value)
  }
  Ok(buffer.to_bytes())
}

///|
pub fn find_tlv(tlvs : Array[RawTlv], type_code : Byte) -> RawTlv? {
  for tlv in tlvs {
    if tlv.type_code == type_code {
      return Some(tlv)
    }
  }
  None
}

///|
pub fn find_all_tlvs(tlvs : Array[RawTlv], type_code : Byte) -> Array[RawTlv] {
  let result = Array::make(0, { type_code: b'\x00', value: Bytes::new(0) })
  for tlv in tlvs {
    if tlv.type_code == type_code {
      result.push(tlv)
    }
  }
  result
}