// 原始邮件切分。
//
// DKIM 必须按原始字节做规范化,任何「先解析成 AST 再序列化」的做法都会丢信息,
// 所以这里保留每个头部字段的原始字节,只把行尾统一成 CRLF。

///|
pub struct HeaderField {
  /// 原始大小写的字段名
  name : String
  /// 原始字段字节(不含末尾换行,内部折叠保留为 CRLF + WSP)
  raw : Bytes
}

///|
pub struct Message {
  headers : Array[HeaderField]
  body : Bytes
}

///|
fn is_wsp(b : Byte) -> Bool {
  b == b' ' || b == b'\t'
}

///|
fn slice_bytes(data : BytesView, start : Int, end : Int) -> Bytes {
  let out : Array[Byte] = []
  for i in start.. Bytes {
  let out : Array[Byte] = []
  let n = input.length()
  let mut i = 0
  while i < n {
    let b = input[i]
    if b == b'\n' && !(i > 0 && input[i - 1] == b'\r') {
      out.push(b'\r')
    }
    out.push(b)
    i = i + 1
  }
  Bytes::from_array(out)
}

///|
/// 解析一封邮件:头部按字段切开,正文保留原始字节。
pub fn parse_message(input : BytesView) -> Message {
  let raw = normalize_crlf(input)
  let n = raw.length()
  let headers : Array[HeaderField] = []
  let mut pos = 0
  let mut body_start = n
  while pos < n {
    // 当前行范围
    let mut line_end = pos
    while line_end < n && raw[line_end] != b'\n' {
      line_end = line_end + 1
    }
    let content_end = if line_end > pos && raw[line_end - 1] == b'\r' {
      line_end - 1
    } else {
      line_end
    }
    if content_end == pos {
      // 空行:头部结束
      body_start = if line_end < n { line_end + 1 } else { line_end }
      break
    }
    // 一个字段从 pos 开始;字段名到冒号为止
    let mut name_end = pos
    while name_end < content_end && raw[name_end] != b':' {
      name_end = name_end + 1
    }
    let name = header_name(raw, pos, name_end)
    let mut field_end = content_end
    let mut next = if line_end < n { line_end + 1 } else { line_end }
    // 继续吸收以 WSP 开头的折叠行
    while next < n {
      let mut folded_end = next
      while folded_end < n && raw[folded_end] != b'\n' {
        folded_end = folded_end + 1
      }
      let folded_content_end = if folded_end > next &&
        raw[folded_end - 1] == b'\r' {
        folded_end - 1
      } else {
        folded_end
      }
      if folded_content_end == next || !is_wsp(raw[next]) {
        break
      }
      field_end = folded_content_end
      next = if folded_end < n { folded_end + 1 } else { folded_end }
    }
    headers.push({ name, raw: slice_bytes(raw, pos, field_end), })
    pos = next
  }
  let body = if body_start < n {
    slice_bytes(raw, body_start, n)
  } else {
    Bytes::new(0)
  }
  { headers, body, }
}

///|
fn header_name(raw : Bytes, start : Int, end : Int) -> String {
  let name = StringBuilder()
  for i in start.. String {
  let out = StringBuilder()
  for c in text {
    if c >= 'A' && c <= 'Z' {
      out.write_char(Int::unsafe_to_char(c.to_int() + 32))
    } else {
      out.write_char(c)
    }
  }
  out.to_string()
}

///|
/// 去掉首尾的 WSP。
pub fn trim_wsp(text : StringView) -> String {
  let chars : Array[Char] = []
  for c in text {
    chars.push(c)
  }
  let mut start = 0
  let mut end = chars.length()
  while start < end && (chars[start] == ' ' || chars[start] == '\t') {
    start = start + 1
  }
  while end > start && (chars[end - 1] == ' ' || chars[end - 1] == '\t') {
    end = end - 1
  }
  let out = StringBuilder()
  for i in start.. Array[HeaderField] {
  let target = ascii_lower(name)
  let out : Array[HeaderField] = []
  for field in self.headers {
    if ascii_lower(field.name) == target {
      out.push(field)
    }
  }
  out
}