///|
fn make_substr(chars : Array[Char], start : Int, end : Int) -> String {
  let buf = StringBuilder::new()
  for i = start; i < end && i < chars.length(); i = i + 1 {
    buf.write_char(chars[i])
  }
  buf.to_string()
}

///|
fn ascii_eq_ignore_case(lhs : Char, rhs : Char) -> Bool {
  lhs.to_ascii_lowercase() == rhs.to_ascii_lowercase()
}

///|
fn is_img_tag_start(chars : Array[Char], i : Int) -> Bool {
  if i + 4 >= chars.length() {
    return false
  }
  chars[i] == '<' &&
  ascii_eq_ignore_case(chars[i + 1], 'i') &&
  ascii_eq_ignore_case(chars[i + 2], 'm') &&
  ascii_eq_ignore_case(chars[i + 3], 'g') &&
  (
    chars[i + 4].is_ascii_whitespace() ||
    chars[i + 4] == '>' ||
    chars[i + 4] == '/'
  )
}

///|
fn is_html_attr_name_char(c : Char) -> Bool {
  !(c.is_ascii_whitespace() || c == '=' || c == '>' || c == '/')
}

///|
fn is_src_attr_name(chars : Array[Char], start : Int, end : Int) -> Bool {
  end - start == 3 &&
  ascii_eq_ignore_case(chars[start], 's') &&
  ascii_eq_ignore_case(chars[start + 1], 'r') &&
  ascii_eq_ignore_case(chars[start + 2], 'c')
}

///|
fn read_html_attr_value(chars : Array[Char], start : Int) -> (String, Int)? {
  if start >= chars.length() {
    return None
  }
  let quote = chars[start]
  if quote == '"' || quote == '\'' {
    let value_start = start + 1
    for i = value_start; i < chars.length(); i = i + 1 {
      if chars[i] == quote {
        return Some((make_substr(chars, value_start, i), i + 1))
      }
    }
    None
  } else {
    let mut value_end = start
    while value_end < chars.length() &&
          !chars[value_end].is_ascii_whitespace() &&
          chars[value_end] != '>' {
      value_end = value_end + 1
    }
    if value_end > start {
      Some((make_substr(chars, start, value_end), value_end))
    } else {
      None
    }
  }
}

///|
/// Extract img src attributes from HTML without requiring a full DOM parse.
pub fn extract_img_srcs(html : String) -> Array[String] {
  let srcs : Array[String] = []
  let chars = html.to_array()
  let len = chars.length()
  let mut i = 0
  while i < len {
    if is_img_tag_start(chars, i) {
      let mut j = i + 4
      while j < len && chars[j] != '>' {
        while j < len && chars[j].is_ascii_whitespace() {
          j = j + 1
        }
        if j >= len || chars[j] == '>' {
          break
        }
        if chars[j] == '/' {
          j = j + 1
          continue
        }
        let name_start = j
        while j < len && is_html_attr_name_char(chars[j]) {
          j = j + 1
        }
        let name_end = j
        while j < len && chars[j].is_ascii_whitespace() {
          j = j + 1
        }
        if j < len && chars[j] == '=' {
          j = j + 1
          while j < len && chars[j].is_ascii_whitespace() {
            j = j + 1
          }
          match read_html_attr_value(chars, j) {
            Some((value, next_idx)) => {
              if is_src_attr_name(chars, name_start, name_end) {
                srcs.push(value)
              }
              j = next_idx
            }
            None => ()
          }
        }
      }
      i = j
    } else {
      i = i + 1
    }
  }
  srcs
}