// STL 各入口共用的输入契约,不包含网格几何有效性判定。

///|
fn stl_binary_length_error(count : UInt, actual : UInt64) -> String? {
  if actual < 84UL {
    return Some(
      "二进制 STL 至少要 84 字节(80 字节文件头 + 4 字节三角形数),这个文件只有 \{actual} 字节",
    )
  }
  let expected = 84UL + count.to_uint64() * 50UL
  if actual < expected {
    return Some(
      "二进制 STL 头部声称有 \{count} 个三角形(需要 \{expected} 字节),但文件只有 \{actual} 字节,文件被截断了",
    )
  }
  if actual > expected {
    return Some(
      "二进制 STL 头部声称有 \{count} 个三角形(需要 \{expected} 字节),但文件有 \{actual} 字节,含多余尾部字节",
    )
  }
  if count == 0U {
    return Some("这个二进制 STL 里一个三角形都没有")
  }
  None
}

///|
fn stl_binary_count(data : Bytes) -> Result[Int, String] {
  let count = if data.length() >= 84 { read_u32_le(data, 80) } else { 0U }
  if stl_binary_length_error(count, data.length().to_uint64()) is Some(message) {
    return Err(message)
  }
  if count > 2147483647U {
    return Err("二进制 STL 三角形数量超出当前实现支持范围")
  }
  Ok(count.reinterpret_as_int())
}

///|
fn stl_binary_record_error(data : Bytes, base : Int, index : Int) -> String? {
  for offset in [0, 12, 24, 36] {
    if !finite_vertex(read_vec3_f32(data, base + offset)) {
      return Some(
        "二进制 STL 第 \{index + 1} 个三角形的坐标或法线不是有限数字",
      )
    }
  }
  None
}

///|
priv enum StlAsciiStage {
  Outside
  NeedLoop
  Vertices
  NeedEndfacet
} derive(Eq)

///|
priv struct StlAsciiReader {
  mut stage : StlAsciiStage
  mut normal : Vec3
  pending : Array[Vec3]
  mut line_no : Int
}

///|
fn stl_ascii_tokens(line : StringView) -> Array[StringView] {
  let tokens = []
  let mut pos = 0
  while pos < line.length() {
    if is_ascii_space(line.code_unit_at(pos).to_int()) {
      pos = pos + 1
      continue
    }
    let start = pos
    while pos < line.length() &&
          !is_ascii_space(line.code_unit_at(pos).to_int()) {
      pos = pos + 1
    }
    tokens.push(line[start:pos])
  }
  tokens
}

///|
fn StlAsciiReader::read_line(
  self : StlAsciiReader,
  line : StringView,
) -> Result[Triangle?, String] {
  self.line_no = self.line_no + 1
  let tokens = stl_ascii_tokens(line)
  let error = "第 \{self.line_no} 行:ASCII STL 记录顺序或字段不合法"
  match tokens {
    [] => ()
    ["solid", ..] | ["endsolid", ..] =>
      if self.stage != Outside {
        return Err("第 \{self.line_no} 行:facet 尚未结束")
      }
    ["facet", "normal", x, y, z] => {
      if self.stage != Outside {
        return Err("第 \{self.line_no} 行:前一个 facet 尚未结束")
      }
      self.normal = match parse_vec3(x, y, z, self.line_no) {
        Ok(v) => v
        Err(message) => return Err(message)
      }
      self.pending.clear()
      self.stage = NeedLoop
    }
    ["outer", "loop"] => {
      if self.stage != NeedLoop {
        return Err(error)
      }
      self.stage = Vertices
    }
    ["vertex", x, y, z] => {
      if self.stage != Vertices {
        return Err(error)
      }
      if self.pending.length() == 3 {
        return Err(
          "第 \{self.line_no} 行:一个面超过 3 个顶点,应该是 3 个",
        )
      }
      match parse_vec3(x, y, z, self.line_no) {
        Ok(v) => self.pending.push(v)
        Err(message) => return Err(message)
      }
    }
    ["endloop"] => {
      if self.stage != Vertices {
        return Err(error)
      }
      if self.pending.length() != 3 {
        return Err(
          "第 \{self.line_no} 行:一个面里面只有 \{self.pending.length()} 个顶点,应该是 3 个",
        )
      }
      self.stage = NeedEndfacet
    }
    ["endfacet"] => {
      if self.stage != NeedEndfacet {
        return Err(error)
      }
      let t = {
        normal: self.normal,
        a: self.pending[0],
        b: self.pending[1],
        c: self.pending[2],
      }
      self.pending.clear()
      self.stage = Outside
      return Ok(Some(t))
    }
    _ => return Err(error)
  }
  Ok(None)
}

///|
// 每次只构造一个三角形;流式统计不保留完整 Mesh。
fn walk_stl_ascii(
  text : String,
  emit : (Triangle) -> Unit,
) -> Result[Int, String] {
  let reader : StlAsciiReader = {
    stage: Outside,
    normal: Vec3::zero(),
    pending: [],
    line_no: 0,
  }
  let mut count = 0
  for line in text.split("\n") {
    match reader.read_line(line) {
      Err(message) => return Err(message)
      Ok(Some(triangle)) => {
        emit(triangle)
        count = count + 1
      }
      Ok(None) => ()
    }
  }
  if reader.stage != Outside {
    return Err(
      "ASCII STL 文件结束时 facet 尚未结束(最后读取第 \{reader.line_no} 行)",
    )
  }
  if count == 0 {
    return Err(
      "这个 ASCII STL 里一个三角形都没有,可能不是 STL 文件",
    )
  }
  Ok(count)
}