///|
pub struct HarDocument {
  version_value : String
  creator_value : HarSoftware
  browser_value : HarSoftware?
  pages_value : Array[HarPage]
  entries_value : Array[HarEntry]
  comment_value : String?
} derive(Eq, Debug)

///|
pub fn HarDocument::version(self : HarDocument) -> String {
  self.version_value
}

///|
pub fn HarDocument::creator(self : HarDocument) -> HarSoftware {
  self.creator_value
}

///|
pub fn HarDocument::browser(self : HarDocument) -> HarSoftware? {
  self.browser_value
}

///|
pub fn HarDocument::pages(self : HarDocument) -> Array[HarPage] {
  self.pages_value.copy()
}

///|
pub fn HarDocument::entries(self : HarDocument) -> Array[HarEntry] {
  self.entries_value.copy()
}

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

///|
pub fn HarDocument::page_count(self : HarDocument) -> Int {
  self.pages_value.length()
}

///|
pub fn HarDocument::entry_count(self : HarDocument) -> Int {
  self.entries_value.length()
}

///|
pub fn HarDocument::entries_for_page(
  self : HarDocument,
  page_id : String,
) -> Array[HarEntry] {
  let result : Array[HarEntry] = []
  for entry in self.entries_value {
    if entry.is_page_entry(page_id) {
      result.push(entry)
    }
  }
  result
}

///|
pub fn HarDocument::unassigned_entries(self : HarDocument) -> Array[HarEntry] {
  let result : Array[HarEntry] = []
  for entry in self.entries_value {
    if entry.page_ref() is None {
      result.push(entry)
    }
  }
  result
}

///|
pub fn HarDocument::find_page(self : HarDocument, id : String) -> HarPage? {
  for page in self.pages_value {
    if page.id() == id {
      return Some(page)
    }
  }
  None
}

///|
pub fn HarDocument::successful_entry_count(self : HarDocument) -> Int {
  let mut count = 0
  for entry in self.entries_value {
    if entry.response().is_success() {
      count += 1
    }
  }
  count
}

///|
pub fn HarDocument::error_entry_count(self : HarDocument) -> Int {
  let mut count = 0
  for entry in self.entries_value {
    if entry.response().is_error() {
      count += 1
    }
  }
  count
}

///|
fn parse_page_array(
  cursors : Array[JsonCursor],
) -> Result[Array[HarPage], HarError] {
  let pages : Array[HarPage] = []
  for cursor in cursors {
    match parse_page_cursor(cursor) {
      Ok(page) => pages.push(page)
      Err(error) => return Err(error)
    }
  }
  Ok(pages)
}

///|
fn parse_entry_array(
  cursors : Array[JsonCursor],
) -> Result[Array[HarEntry], HarError] {
  let entries : Array[HarEntry] = []
  for cursor in cursors {
    match parse_entry_cursor(cursor) {
      Ok(entry) => entries.push(entry)
      Err(error) => return Err(error)
    }
  }
  Ok(entries)
}

///|
fn parse_log_cursor(cursor : JsonCursor) -> Result[HarDocument, HarError] {
  let version = match cursor.required_string("version") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  if version != "1.2" {
    return Err(
      HarError::new(
        InvalidVersion,
        append_json_field(cursor.path(), "version"),
        "this package accepts HAR version 1.2",
      ),
    )
  }
  let creator_cursor = match cursor.required_object("creator") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let creator = match parse_software_cursor(creator_cursor) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let browser_cursor = match cursor.optional_object("browser") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let browser = match browser_cursor {
    Some(value) =>
      match parse_software_cursor(value) {
        Ok(software) => Some(software)
        Err(error) => return Err(error)
      }
    None => None
  }
  let page_cursors = match cursor.optional_array("pages") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let pages = match parse_page_array(page_cursors) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let entry_cursors = match cursor.required_array("entries") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let entries = match parse_entry_array(entry_cursors) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let comment = match cursor.optional_string("comment") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  Ok({
    version_value: version,
    creator_value: creator,
    browser_value: browser,
    pages_value: pages,
    entries_value: entries,
    comment_value: comment,
  })
}

///|
pub fn parse_har(input : String) -> Result[HarDocument, HarError] {
  let root = match parse_json_cursor(input) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let log = match root.required_object("log") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  parse_log_cursor(log)
}