///|
pub struct HarTimings {
  blocked_value : Double?
  dns_value : Double?
  connect_value : Double?
  send_value : Double
  wait_value : Double
  receive_value : Double
  ssl_value : Double?
  comment_value : String?
} derive(Eq, Debug)

///|
pub fn HarTimings::new(
  blocked? : Double,
  dns? : Double,
  connect? : Double,
  send : Double,
  wait : Double,
  receive : Double,
  ssl? : Double,
  comment? : String,
) -> Result[HarTimings, HarError] {
  build_timings(
    blocked, dns, connect, send, wait, receive, ssl, comment, "$.timings",
  )
}

///|
pub fn parse_timings(input : String) -> Result[HarTimings, HarError] {
  let cursor = match parse_json_cursor(input) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  parse_timings_cursor(cursor)
}

///|
fn build_timings(
  blocked : Double?,
  dns : Double?,
  connect : Double?,
  send : Double,
  wait : Double,
  receive : Double,
  ssl : Double?,
  comment : String?,
  path : String,
) -> Result[HarTimings, HarError] {
  for item in [("send", send), ("wait", wait), ("receive", receive)] {
    let (name, value) = item
    if value < 0.0 {
      return Err(
        HarError::new(
          InvalidTiming,
          append_json_field(path, name),
          name + " must be non-negative",
        ),
      )
    }
  }
  for
    item in [
      ("blocked", blocked),
      ("dns", dns),
      ("connect", connect),
      ("ssl", ssl),
    ] {
    let (name, value) = item
    match value {
      Some(number) =>
        if number < -1.0 {
          return Err(
            HarError::new(
              InvalidTiming,
              append_json_field(path, name),
              name + " must be -1 or non-negative",
            ),
          )
        }
      None => ()
    }
  }
  Ok({
    blocked_value: normalize_optional_timing(blocked),
    dns_value: normalize_optional_timing(dns),
    connect_value: normalize_optional_timing(connect),
    send_value: send,
    wait_value: wait,
    receive_value: receive,
    ssl_value: normalize_optional_timing(ssl),
    comment_value: comment,
  })
}

///|
fn normalize_optional_timing(value : Double?) -> Double? {
  match value {
    Some(number) => if number == -1.0 { None } else { Some(number) }
    None => None
  }
}

///|
pub fn HarTimings::blocked(self : HarTimings) -> Double? {
  self.blocked_value
}

///|
pub fn HarTimings::dns(self : HarTimings) -> Double? {
  self.dns_value
}

///|
pub fn HarTimings::connect(self : HarTimings) -> Double? {
  self.connect_value
}

///|
pub fn HarTimings::send(self : HarTimings) -> Double {
  self.send_value
}

///|
pub fn HarTimings::wait(self : HarTimings) -> Double {
  self.wait_value
}

///|
pub fn HarTimings::receive(self : HarTimings) -> Double {
  self.receive_value
}

///|
pub fn HarTimings::ssl(self : HarTimings) -> Double? {
  self.ssl_value
}

///|
pub fn HarTimings::comment(self : HarTimings) -> String? {
  self.comment_value
}

///|
pub fn HarTimings::known_total(self : HarTimings) -> Double {
  let mut total = self.send_value + self.wait_value + self.receive_value
  match self.blocked_value {
    Some(value) => total += value
    None => ()
  }
  match self.dns_value {
    Some(value) => total += value
    None => ()
  }
  match self.connect_value {
    Some(value) => total += value
    None => ()
  }
  total
}

///|
pub fn HarTimings::network_total(self : HarTimings) -> Double {
  let mut total = self.send_value + self.wait_value + self.receive_value
  match self.dns_value {
    Some(value) => total += value
    None => ()
  }
  match self.connect_value {
    Some(value) => total += value
    None => ()
  }
  total
}

///|
pub fn HarTimings::transport_total(self : HarTimings) -> Double {
  let mut total = self.send_value + self.receive_value
  match self.connect_value {
    Some(value) => total += value
    None => ()
  }
  total
}

///|
pub fn HarTimings::has_ssl(self : HarTimings) -> Bool {
  self.ssl_value is Some(_)
}

///|
pub fn HarTimings::dominant_phase(self : HarTimings) -> String {
  let mut name = "send"
  let mut maximum = self.send_value
  if self.wait_value > maximum {
    name = "wait"
    maximum = self.wait_value
  }
  if self.receive_value > maximum {
    name = "receive"
    maximum = self.receive_value
  }
  match self.blocked_value {
    Some(value) =>
      if value > maximum {
        name = "blocked"
        maximum = value
      }
    None => ()
  }
  match self.dns_value {
    Some(value) =>
      if value > maximum {
        name = "dns"
        maximum = value
      }
    None => ()
  }
  match self.connect_value {
    Some(value) => if value > maximum { name = "connect" }
    None => ()
  }
  name
}

///|
fn parse_timings_cursor(cursor : JsonCursor) -> Result[HarTimings, HarError] {
  let blocked = match cursor.optional_number("blocked") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let dns = match cursor.optional_number("dns") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let connect = match cursor.optional_number("connect") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let send = match cursor.required_number("send") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let wait = match cursor.required_number("wait") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let receive = match cursor.required_number("receive") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let ssl = match cursor.optional_number("ssl") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let comment = match cursor.optional_string("comment") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  match
    build_timings(
      blocked,
      dns,
      connect,
      send,
      wait,
      receive,
      ssl,
      comment,
      cursor.path(),
    ) {
    Ok(value) => Ok(value)
    Err(error) => Err(error)
  }
}