///|
/// HTTP Response representation
pub(all) struct Response {
  version : String
  status_code : Int
  reason_phrase : String
  headers : Array[(String, String)]
  body : Array[Byte]
} derive(Show, Eq)

///|
pub fn Response::new(status_code : Int, reason_phrase : String) -> Response {
  { version: "HTTP/1.1", status_code, reason_phrase, headers: [], body: [] }
}

///|
pub fn Response::with_version(
  version : String,
  status_code : Int,
  reason_phrase : String,
) -> Response {
  { version, status_code, reason_phrase, headers: [], body: [] }
}

///|
pub fn Response::is_success(self : Response) -> Bool {
  self.status_code >= 200 && self.status_code < 300
}

///|
pub fn Response::is_redirect(self : Response) -> Bool {
  self.status_code >= 300 && self.status_code < 400
}

///|
pub fn Response::is_client_error(self : Response) -> Bool {
  self.status_code >= 400 && self.status_code < 500
}

///|
pub fn Response::is_server_error(self : Response) -> Bool {
  self.status_code >= 500 && self.status_code < 600
}

///|
pub fn Response::header(
  self : Response,
  name : String,
  value : String,
) -> Response {
  let new_headers = []
  for h in self.headers {
    new_headers.push(h)
  }
  new_headers.push((name, value))
  { ..self, headers: new_headers }
}

///|
pub fn Response::body(self : Response, body : Array[Byte]) -> Response {
  { ..self, body, }
}

///|
pub fn Response::get_header(self : Response, name : String) -> String? {
  let lower_name = response_string_to_lower(name)
  let mut idx = 0
  while idx < self.headers.length() {
    let (key, value) = self.headers[idx]
    if response_string_to_lower(key) == lower_name {
      return Some(value)
    }
    idx = idx + 1
  }
  None
}

///|
pub fn Response::has_header(self : Response, name : String) -> Bool {
  let lower_name = response_string_to_lower(name)
  let mut idx = 0
  while idx < self.headers.length() {
    let (key, _) = self.headers[idx]
    if response_string_to_lower(key) == lower_name {
      return true
    }
    idx = idx + 1
  }
  false
}

///|
pub fn Response::is_keep_alive(self : Response) -> Bool {
  if self.version == "HTTP/1.1" {
    match self.get_header("connection") {
      Some(v) => response_string_to_lower(v) != "close"
      None => true
    }
  } else if self.version == "HTTP/1.0" {
    match self.get_header("connection") {
      Some(v) => response_string_to_lower(v) == "keep-alive"
      None => false
    }
  } else {
    false
  }
}

///|
pub fn Response::content_length(self : Response) -> Int? {
  match self.get_header("content-length") {
    Some(v) => response_parse_int(v)
    None => None
  }
}

///|
pub fn Response::is_chunked(self : Response) -> Bool {
  match self.get_header("transfer-encoding") {
    Some(v) => response_string_to_lower(v) == "chunked"
    None => false
  }
}

///|
fn response_string_to_lower(s : String) -> String {
  let mut result = ""
  let mut idx = 0
  while idx < s.length() {
    let c = s[idx]
    let ci = c.to_int()
    let new_c = if ci >= 65 && ci <= 90 {
      Int::unsafe_to_char(ci + 32).to_string()
    } else {
      c.to_string()
    }
    result = result + new_c
    idx = idx + 1
  }
  result
}

///|
fn response_parse_int(s : String) -> Int? {
  let mut result = 0
  let mut idx = 0
  let len = s.length()
  if len == 0 {
    return None
  }
  while idx < len {
    let c = s[idx]
    let ci = c.to_int()
    if ci >= 48 && ci <= 57 {
      result = result * 10 + (ci - 48)
    } else {
      return None
    }
    idx = idx + 1
  }
  Some(result)
}