///|
pub struct SitemapEntry {
  loc : String
  lastmod : String
  changefreq : String
  priority : String
} derive(Debug, Eq)

///|
pub fn sitemap_urls(xml : String) -> Array[String] {
  let urls : Array[String] = []
  for entry in sitemap_entries(xml) {
    urls.push(entry.loc)
  }
  urls
}

///|
pub fn sitemap_entries(xml : String) -> Array[SitemapEntry] {
  let entries : Array[SitemapEntry] = []
  let blocks = xml_blocks(xml, "url")
  for block in blocks {
    let loc = xml_tag_text(block, "loc")
    if loc != "" {
      entries.push({
        loc,
        lastmod: xml_tag_text(block, "lastmod"),
        changefreq: xml_tag_text(block, "changefreq"),
        priority: xml_tag_text(block, "priority"),
      })
    }
  }
  entries
}

///|
pub fn sitemap_index_locations(xml : String) -> Array[String] {
  let urls : Array[String] = []
  let blocks = xml_blocks(xml, "sitemap")
  for block in blocks {
    let loc = xml_tag_text(block, "loc")
    if loc != "" {
      urls.push(loc)
    }
  }
  urls
}

///|
pub fn xml_blocks(xml : String, tag : String) -> Array[String] {
  let blocks : Array[String] = []
  let open = "<" + tag
  let close = ""
  let mut current = xml
  while true {
    match current.find(open) {
      Some(start) => {
        let after_open = current.unsafe_substring(start~, end=current.length())
        match after_open.find(">") {
          Some(gt) => {
            let body_start = gt + 1
            let body_rest = after_open.unsafe_substring(
              start=body_start,
              end=after_open.length(),
            )
            match body_rest.find(close) {
              Some(end) => {
                blocks.push(body_rest.unsafe_substring(start=0, end~))
                current = body_rest.unsafe_substring(
                  start=end + close.length(),
                  end=body_rest.length(),
                )
                continue
              }
              None => break
            }
          }
          None => break
        }
      }
      None => break
    }
  }
  blocks
}

///|
pub fn xml_tag_text(xml : String, tag : String) -> String {
  let open = "<" + tag
  let close = ""
  match xml.find(open) {
    Some(start) => {
      let after_open = xml.unsafe_substring(start~, end=xml.length())
      match after_open.find(">") {
        Some(gt) => {
          let body = after_open.unsafe_substring(
            start=gt + 1,
            end=after_open.length(),
          )
          match body.find(close) {
            Some(end) =>
              xml_unescape(trim_ascii(body.unsafe_substring(start=0, end~)))
            None => ""
          }
        }
        None => ""
      }
    }
    None => ""
  }
}

///|
pub fn xml_unescape(text : String) -> String {
  text_replace(
    text_replace(
      text_replace(text_replace(text, "&", "&"), "<", "<"),
      ">",
      ">",
    ),
    """,
    "\"",
  )
}

///|
pub fn text_replace(text : String, from : String, to : String) -> String {
  if from == "" {
    return text
  }
  let mut out = ""
  let mut current = text
  while true {
    match current.find(from) {
      Some(index) => {
        out = out + current.unsafe_substring(start=0, end=index) + to
        current = current.unsafe_substring(
          start=index + from.length(),
          end=current.length(),
        )
        continue
      }
      None => {
        out = out + current
        break
      }
    }
  }
  out
}

///|
pub fn sitemap_report(xml : String) -> String {
  let entries = sitemap_entries(xml)
  let indexes = sitemap_index_locations(xml)
  "urls=" +
  entries.length().to_string() +
  "\nindexes=" +
  indexes.length().to_string()
}