///|
fn xhtml_namespace() -> String {
  "http://www.w3.org/1999/xhtml"
}

///|
fn xhtml_xml_namespace() -> String {
  "http://www.w3.org/XML/1998/namespace"
}

///|
fn xhtml_copy_references(references : Array[String]) -> Array[String] {
  let copy : Array[String] = []
  for reference in references {
    copy.push(reference)
  }
  copy
}

///|
priv struct AtomXhtmlCapture {
  mut valid : Bool
  mut div_seen : Bool
  mut div_open : Bool
  mut plain_text : String
  mut markup : String
  mut language : String?
  base_references : Array[String]
  container_attributes : Array[XhtmlAttribute]
}

///|
fn AtomXhtmlCapture::new(
  language : String?,
  base_references : Array[String],
) -> AtomXhtmlCapture {
  {
    valid: true,
    div_seen: false,
    div_open: false,
    plain_text: "",
    markup: "",
    language,
    base_references: xhtml_copy_references(base_references),
    container_attributes: [],
  }
}

///|
fn xhtml_other_namespace(uri : String, namespaces : Array[String]) -> Unit {
  if uri == xhtml_namespace() || uri == xhtml_xml_namespace() {
    return
  }
  for existing in namespaces {
    if existing == uri {
      return
    }
  }
  namespaces.push(uri)
}

///|
fn xhtml_name(name : @xml.XmlName, namespaces : Array[String]) -> String {
  match name.namespace_uri {
    None => name.local_name
    Some(uri) if uri == xhtml_namespace() => "h:" + name.local_name
    Some(uri) if uri == xhtml_xml_namespace() => "xml:" + name.local_name
    Some(uri) => {
      for index = 0; index < namespaces.length(); index = index + 1 {
        if namespaces[index] == uri {
          return "n" + index.to_string() + ":" + name.local_name
        }
      }
      name.local_name
    }
  }
}

///|
fn xhtml_start_tag(element : @xml.NamespaceElement, empty : Bool) -> String {
  let namespaces : Array[String] = []
  match element.name.namespace_uri {
    Some(uri) => xhtml_other_namespace(uri, namespaces)
    None => ()
  }
  for attribute in element.attributes {
    match attribute.name.namespace_uri {
      Some(uri) => xhtml_other_namespace(uri, namespaces)
      None => ()
    }
  }
  let mut tag = "<" +
    xhtml_name(element.name, namespaces) +
    " xmlns:h=\"" +
    xhtml_namespace() +
    "\""
  for index = 0; index < namespaces.length(); index = index + 1 {
    tag = tag +
      " xmlns:n" +
      index.to_string() +
      "=\"" +
      @xml.escape(namespaces[index]) +
      "\""
  }
  for attribute in element.attributes {
    tag = tag +
      " " +
      xhtml_name(attribute.name, namespaces) +
      "=\"" +
      @xml.escape(attribute.value) +
      "\""
  }
  tag + (if empty { "/>" } else { ">" })
}

///|
fn xhtml_end_tag(name : @xml.XmlName) -> String {
  let namespaces : Array[String] = []
  match name.namespace_uri {
    Some(uri) => xhtml_other_namespace(uri, namespaces)
    None => ()
  }
  ""
}

///|
fn AtomXhtmlCapture::node(
  self : AtomXhtmlCapture,
  element : @xml.NamespaceElement,
  is_root : Bool,
  empty : Bool,
) -> Unit {
  if is_root {
    if self.div_seen ||
      element.name.local_name != "div" ||
      element.name.namespace_uri != Some(xhtml_namespace()) {
      self.valid = false
      return
    }
    self.div_seen = true
    self.div_open = !empty
    for attribute in element.attributes {
      self.container_attributes.push({
        local_name: attribute.name.local_name,
        namespace_uri: attribute.name.namespace_uri,
        value: attribute.value,
      })
    }
    match xml_language_attribute(element) {
      Some(value) => self.language = non_blank_text(value)
      None => ()
    }
    match xml_base_attribute(element) {
      Some(value) => self.base_references.push(value)
      None => ()
    }
  } else if self.div_open {
    self.markup = self.markup + xhtml_start_tag(element, empty)
  } else {
    self.valid = false
  }
}

///|
fn AtomXhtmlCapture::text(
  self : AtomXhtmlCapture,
  value : String,
  inside_div : Bool,
) -> Unit {
  if inside_div && self.div_open {
    self.plain_text = self.plain_text + value
    self.markup = self.markup + @xml.escape(value)
  } else if !value.trim().is_empty() {
    self.valid = false
  }
}

///|
fn AtomXhtmlCapture::comment(self : AtomXhtmlCapture, value : String) -> Unit {
  if self.div_open {
    self.markup = self.markup + ""
  }
}

///|
fn AtomXhtmlCapture::processing_instruction(
  self : AtomXhtmlCapture,
  target : String,
  data : String,
) -> Unit {
  if self.div_open {
    self.markup = self.markup +
      ""
  }
}

///|
fn AtomXhtmlCapture::end(
  self : AtomXhtmlCapture,
  name : @xml.XmlName,
  is_root : Bool,
) -> Unit {
  if is_root {
    self.div_open = false
  } else if self.div_open {
    self.markup = self.markup + xhtml_end_tag(name)
  }
}

///|
fn AtomXhtmlCapture::fragment(self : AtomXhtmlCapture) -> XhtmlFragment? {
  if !self.valid || !self.div_seen || self.div_open {
    return None
  }
  Some({
    markup: self.markup,
    language: self.language,
    base_references: self.base_references,
    container_attributes: self.container_attributes,
  })
}