// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0

///|
/// The demo's raw multipart text, as bytes.
fn bytes_of(s : String) -> Bytes {
  let out : Array[Byte] = []
  for i = 0; i < s.length(); i = i + 1 {
    out.push((s[i].to_int() & 0xFF).to_byte())
  }
  Bytes::from_array(out)
}

///|
/// A string as a JSON value, quotes and backslashes escaped.
fn jstr(s : String) -> String {
  let b = StringBuilder()
  b.write_char('"')
  for i = 0; i < s.length(); i = i + 1 {
    let c = s[i]
    if c == '"' {
      b.write_string("\\\"")
    } else if c == '\\' {
      b.write_string("\\\\")
    } else if c == '\n' {
      b.write_string("\\n")
    } else if c == '\r' {
      b.write_string("\\r")
    } else if c == '\t' {
      b.write_string("\\t")
    } else {
      b.write_view(s[i:i + 1])
    }
  }
  b.write_char('"')
  b.to_string()
}

///|
/// The media type a filename's extension implies.
pub fn browser_guess_type(name : String) -> String {
  @moonmime.guess_type(name[:])
}

///|
/// The common extension for a media type, or empty when unknown.
pub fn browser_guess_extension(value : String) -> String {
  match @moonmime.guess_extension(value[:]) {
    Some(e) => e
    None => ""
  }
}

///|
/// A `Content-Type` header, parsed: its essence, type, subtype, and the value
/// it writes back as.
pub fn browser_media_type(header : String) -> String {
  let mt = @moonmime.media_type(header[:])
  let essence = if mt.subtype() == "" {
    mt.type_()
  } else {
    mt.type_() + "/" + mt.subtype()
  }
  "{\"essence\":" +
  jstr(essence) +
  ",\"type\":" +
  jstr(mt.type_()) +
  ",\"subtype\":" +
  jstr(mt.subtype()) +
  ",\"string\":" +
  jstr(mt.to_string()) +
  "}"
}

///|
/// A `Content-Disposition` header that names `filename`; non-ASCII goes out as
/// `filename*` (RFC 5987).
pub fn browser_content_disposition(filename : String, inline : Bool) -> String {
  @moonmime.content_disposition(filename[:], inline~)
}

///|
/// Split a multipart body into its parts and report each part's
/// `Content-Disposition` (name, filename), content type, size, and body text.
pub fn browser_multipart(body : String, boundary : String) -> String {
  try {
    let parts = @moonmime.multipart(bytes_of(body)[:], boundary[:])
    let b = StringBuilder()
    b.write_char('[')
    for i = 0; i < parts.length(); i = i + 1 {
      if i > 0 {
        b.write_char(',')
      }
      let p = parts[i]
      let d = p.disposition()
      b.write_char('{')
      b.write_string("\"name\":")
      match d {
        Some(d) =>
          match d.param("name") {
            Some(n) => b.write_string(jstr(n))
            None => b.write_string("null")
          }
        None => b.write_string("null")
      }
      b.write_string(",\"filename\":")
      match d {
        Some(d) =>
          match d.filename() {
            Some(f) => b.write_string(jstr(f))
            None => b.write_string("null")
          }
        None => b.write_string("null")
      }
      b.write_string(",\"content_type\":")
      match p.content_type() {
        Some(mt) => b.write_string(jstr(mt.to_string()))
        None => b.write_string("\"\"")
      }
      b.write_string(",\"size\":")
      b.write_string(p.body.length().to_string())
      b.write_string(",\"body\":")
      b.write_string(jstr(@utf8.decode_lossy(p.body[:])))
      b.write_char('}')
    }
    b.write_char(']')
    return b.to_string()
  } catch {
    _ => return "{\"error\":true}"
  }
}

///|
/// Read a `multipart/form-data` body into its text fields and its files.
pub fn browser_formdata(body : String, boundary : String) -> String {
  try {
    let form = @moonmime.formdata(bytes_of(body)[:], boundary[:])
    let b = StringBuilder()
    b.write_string("{\"fields\":[")
    let fields = form.entries()
    for i = 0; i < fields.length(); i = i + 1 {
      if i > 0 {
        b.write_char(',')
      }
      b.write_string("{\"name\":")
      b.write_string(jstr(fields[i].name))
      b.write_string(",\"value\":")
      b.write_string(jstr(fields[i].value))
      b.write_char('}')
    }
    b.write_string("],\"files\":[")
    let files = form.uploads()
    for i = 0; i < files.length(); i = i + 1 {
      if i > 0 {
        b.write_char(',')
      }
      b.write_string("{\"name\":")
      b.write_string(jstr(files[i].name))
      b.write_string(",\"filename\":")
      b.write_string(jstr(files[i].filename))
      b.write_string(",\"content_type\":")
      b.write_string(jstr(files[i].content_type))
      b.write_string(",\"size\":")
      b.write_string(files[i].size().to_string())
      b.write_char('}')
    }
    b.write_string("],\"is_empty\":")
    b.write_string(if form.is_empty() { "true" } else { "false" })
    b.write_char('}')
    return b.to_string()
  } catch {
    _ => return "{\"error\":true}"
  }
}

///|
/// entry point required by the executable package; the page calls the
/// exported functions instead.
fn main {
  ignore(browser_guess_type("demo.png"))
}