///|
/// Encode an HTTP request to bytes
pub fn encode_request(request : Request) -> Array[Byte] {
  let headers_part = encode_request_headers(request)
  let body_part = request.body
  array_append(headers_part, body_part)
}

///|
/// Encode an HTTP response to bytes
pub fn encode_response(response : Response) -> Array[Byte] {
  let headers_part = encode_response_headers(response)
  let body_part = response.body
  array_append(headers_part, body_part)
}

///|
/// Encode only the headers part of an HTTP request
pub fn encode_request_headers(request : Request) -> Array[Byte] {
  let line = request.http_method +
    " " +
    request.uri +
    " " +
    request.version +
    "\r\n"
  let mut result = string_to_bytes(line)
  for item in request.headers {
    let (name, value) = item
    let h = name + ": " + value + "\r\n"
    result = array_append(result, string_to_bytes(h))
  }
  result = array_append(result, string_to_bytes("\r\n"))
  result
}

///|
/// Encode only the headers part of an HTTP response
pub fn encode_response_headers(response : Response) -> Array[Byte] {
  let line = response.version +
    " " +
    response.status_code.to_string() +
    " " +
    response.reason_phrase +
    "\r\n"
  let mut result = string_to_bytes(line)
  for item in response.headers {
    let (name, value) = item
    let h = name + ": " + value + "\r\n"
    result = array_append(result, string_to_bytes(h))
  }
  result = array_append(result, string_to_bytes("\r\n"))
  result
}

///|
/// Encode a single chunk of chunked transfer encoding
pub fn encode_chunk(data : Array[Byte]) -> Array[Byte] {
  let size = data.length()
  let size_str = int_to_hex(size) + "\r\n"
  let mut result = string_to_bytes(size_str)
  result = array_append(result, data)
  result = array_append(result, string_to_bytes("\r\n"))
  result
}

///|
/// Encode multiple chunks of chunked transfer encoding
pub fn encode_chunks(chunks : Array[Array[Byte]]) -> Array[Byte] {
  let mut result = []
  for chunk in chunks {
    result = array_append(result, encode_chunk(chunk))
  }
  result = array_append(result, string_to_bytes("0\r\n\r\n"))
  result
}

///|
fn array_append(a : Array[Byte], b : Array[Byte]) -> Array[Byte] {
  let result = a
  for byte in b {
    result.push(byte)
  }
  result
}

///|
fn string_to_bytes(s : String) -> Array[Byte] {
  let result = []
  let mut idx = 0
  while idx < s.length() {
    result.push(s[idx].to_int().to_byte())
    idx = idx + 1
  }
  result
}

///|
fn int_to_hex(n : Int) -> String {
  let hex = "0123456789abcdef"
  if n == 0 {
    return "0"
  }
  let mut result = ""
  let mut num = n
  while num > 0 {
    let digit = num % 16
    result = hex[digit].to_string() + result
    num = num / 16
  }
  result
}