// SPDX-License-Identifier: Apache-2.0

///|
fn cli_limit(
  matches : @argparse.Matches,
  name : String,
  fallback : Int32,
) -> Result[Int32, String] {
  match matches.values.get(name) {
    Some([raw, ..]) => {
      let value = @string.parse_int(raw) catch {
        _ => return Err("--\{name} 必须是非负 Int32 整数")
      }
      if value < 0 {
        return Err("--\{name} 必须是非负 Int32 整数")
      }
      Ok(value)
    }
    _ => Ok(fallback)
  }
}

///|
fn cli_limits(
  matches : @argparse.Matches,
) -> Result[@stlkit.ParseLimits, String] {
  let defaults = @stlkit.ParseLimits::default()
  let input = match
    cli_limit(matches, "max-input-bytes", defaults.max_input_bytes) {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  let triangles = match
    cli_limit(matches, "max-triangles", defaults.max_triangles) {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  let vertices = match
    cli_limit(matches, "max-vertices", defaults.max_vertices) {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  let uncompressed = match
    cli_limit(
      matches,
      "max-uncompressed-bytes",
      defaults.max_uncompressed_bytes,
    ) {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  if triangles > 715827882 {
    return Err("--max-triangles 不能超过 715827882")
  }
  Ok({
    ..defaults,
    max_input_bytes: input,
    max_triangles: triangles,
    max_vertices: vertices,
    max_uncompressed_bytes: uncompressed,
  })
}

///|
// File size is checked before allocating/reading the full input. The file can
// change concurrently: reject size changes and premature EOF instead of padding.
async fn read_input(
  path : String,
  limits : @stlkit.ParseLimits,
) -> Result[Bytes, String] {
  let file = @asyncfs.open(path, mode=@asyncfs.Mode::ReadOnly) catch {
    err => return Err("读不了文件「\{path}」:\{err}")
  }
  let result = try {
    let size = file.size()
    if size < 0L || size > limits.max_input_bytes.to_int64() {
      Err("resource_limit: max_input_bytes (max=\{limits.max_input_bytes})")
    } else {
      let length = size.to_int()
      let bytes = FixedArray::make(length, b'\x00')
      let mut offset = 0
      let mut failed = false
      while offset < length {
        let n = file.read_at(
          bytes,
          position=offset.to_int64(),
          offset~,
          len=(length - offset).min(262144),
        )
        if n <= 0 {
          failed = true
          break
        }
        offset += n
      }
      if failed || file.size() != size {
        Err("读取时文件长度发生变化或文件被截断")
      } else {
        Ok(Bytes::from_array(bytes))
      }
    }
  } catch {
    err => Err("读不了文件「\{path}」:\{err}")
  }
  let _ = file.close()
  result
}