///|
pub fn generate_vertex_remap_unindexed(
  vertices : Bytes,
  vertex_count : Int,
  vertex_size : Int,
) -> (FixedArray[UInt], Int) raise MeshoptError {
  require(vertex_count >= 0, "vertex count must be non-negative")
  require(vertex_size > 0, "vertex size must be positive")
  require_bytes_length(vertices, vertex_count * vertex_size, "vertices")
  let remap = FixedArray::make(vertex_count, 0U)
  let unique_vertex_count = generate_vertex_remap_unindexed_ffi(
    remap, vertices, vertex_count, vertex_size,
  )
  (remap, unique_vertex_count)
}

///|
pub fn generate_vertex_remap_indexed(
  indices : FixedArray[UInt],
  vertices : Bytes,
  vertex_count : Int,
  vertex_size : Int,
) -> (FixedArray[UInt], Int) raise MeshoptError {
  require(vertex_count >= 0, "vertex count must be non-negative")
  require(vertex_size > 0, "vertex size must be positive")
  require_bytes_length(vertices, vertex_count * vertex_size, "vertices")
  let remap = FixedArray::make(vertex_count, 0U)
  let unique_vertex_count = generate_vertex_remap_indexed_ffi(
    remap,
    indices,
    indices.length(),
    vertices,
    vertex_count,
    vertex_size,
  )
  (remap, unique_vertex_count)
}

///|
pub fn remap_vertex_buffer(
  vertices : Bytes,
  vertex_count : Int,
  vertex_size : Int,
  unique_vertex_count : Int,
  remap : FixedArray[UInt],
) -> Bytes raise MeshoptError {
  require(vertex_count >= 0, "vertex count must be non-negative")
  require(unique_vertex_count >= 0, "unique vertex count must be non-negative")
  require(vertex_size > 0, "vertex size must be positive")
  require_fixedarray_length(remap, vertex_count, "remap")
  require_bytes_length(vertices, vertex_count * vertex_size, "vertices")
  let destination = Bytes::make(unique_vertex_count * vertex_size, 0)
  remap_vertex_buffer_ffi(
    destination, vertices, vertex_count, vertex_size, remap,
  )
  destination
}

///|
pub fn remap_index_buffer_unindexed(
  index_count : Int,
  remap : FixedArray[UInt],
) -> FixedArray[UInt] raise MeshoptError {
  require(index_count >= 0, "index count must be non-negative")
  require_fixedarray_length(remap, index_count, "remap")
  let destination = FixedArray::make(index_count, 0U)
  remap_index_buffer_unindexed_ffi(destination, index_count, remap)
  destination
}

///|
pub fn remap_index_buffer_indexed(
  indices : FixedArray[UInt],
  remap : FixedArray[UInt],
) -> FixedArray[UInt] raise MeshoptError {
  let remap_length = remap.length().reinterpret_as_uint()
  for i in 0.. FixedArray[UInt] raise MeshoptError {
  require(vertex_count >= 0, "vertex count must be non-negative")
  require(vertex_size > 0, "vertex size must be positive")
  require(
    vertex_stride >= vertex_size,
    "vertex stride must be at least vertex size",
  )
  require_bytes_length(vertices, vertex_count * vertex_stride, "vertices")
  let destination = FixedArray::make(indices.length(), 0U)
  generate_shadow_index_buffer_ffi(
    destination,
    indices,
    indices.length(),
    vertices,
    vertex_count,
    vertex_size,
    vertex_stride,
  )
  destination
}

///|
pub fn generate_position_remap(
  positions : FixedArray[Float],
  vertex_count : Int,
  positions_stride : Int,
) -> FixedArray[UInt] raise MeshoptError {
  require_float_stream(
    positions, vertex_count, positions_stride, 12, "positions",
  )
  let destination = FixedArray::make(vertex_count, 0U)
  generate_position_remap_ffi(
    destination, positions, vertex_count, positions_stride,
  )
  destination
}

///|
pub fn generate_adjacency_index_buffer(
  indices : FixedArray[UInt],
  positions : FixedArray[Float],
  vertex_count : Int,
  positions_stride : Int,
) -> FixedArray[UInt] raise MeshoptError {
  require_triangle_indices(indices.length())
  require_float_stream(
    positions, vertex_count, positions_stride, 12, "positions",
  )
  let destination = FixedArray::make(indices.length() * 2, 0U)
  generate_adjacency_index_buffer_ffi(
    destination,
    indices,
    indices.length(),
    positions,
    vertex_count,
    positions_stride,
  )
  destination
}

///|
pub fn generate_tessellation_index_buffer(
  indices : FixedArray[UInt],
  positions : FixedArray[Float],
  vertex_count : Int,
  positions_stride : Int,
) -> FixedArray[UInt] raise MeshoptError {
  require_triangle_indices(indices.length())
  require_float_stream(
    positions, vertex_count, positions_stride, 12, "positions",
  )
  let destination = FixedArray::make(indices.length() * 4, 0U)
  generate_tessellation_index_buffer_ffi(
    destination,
    indices,
    indices.length(),
    positions,
    vertex_count,
    positions_stride,
  )
  destination
}

///|
pub fn generate_provoking_index_buffer(
  indices : FixedArray[UInt],
  vertex_count : Int,
) -> ProvokingIndexBuffer raise MeshoptError {
  require_triangle_indices(indices.length())
  require(vertex_count >= 0, "vertex count must be non-negative")
  let destination = FixedArray::make(indices.length(), 0U)
  let reorder = FixedArray::make(vertex_count + indices.length() / 3, 0U)
  let reorder_count = generate_provoking_index_buffer_ffi(
    destination,
    reorder,
    indices,
    indices.length(),
    vertex_count,
  )
  { indices: destination, reorder, reorder_count }
}

///|
fn require_packed_streams(
  streams : PackedStreams,
  vertex_count : Int,
) -> Unit raise MeshoptError {
  require(vertex_count >= 0, "vertex count must be non-negative")
  require(
    streams.stream_count > 0 && streams.stream_count <= 16,
    "stream count must be in 1..=16",
  )
  require_fixedarray_length(
    streams.offsets,
    streams.stream_count,
    "stream offsets",
  )
  require_fixedarray_length(streams.sizes, streams.stream_count, "stream sizes")
  require_fixedarray_length(
    streams.strides,
    streams.stream_count,
    "stream strides",
  )
  for i in 0..= 0, "stream offset must be non-negative")
    require(streams.sizes[i] > 0, "stream size must be positive")
    require(
      streams.strides[i] >= streams.sizes[i],
      "stream stride must be at least stream size",
    )
    require_bytes_length(
      streams.source,
      streams.offsets[i] + vertex_count * streams.strides[i],
      "packed stream source",
    )
  }
}

///|
pub fn generate_vertex_remap_multi_unindexed(
  streams : PackedStreams,
  vertex_count : Int,
) -> (FixedArray[UInt], Int) raise MeshoptError {
  require_packed_streams(streams, vertex_count)
  let remap = FixedArray::make(vertex_count, 0U)
  let unique_vertex_count = generate_vertex_remap_multi_unindexed_ffi(
    remap,
    streams.source,
    vertex_count,
    streams.offsets,
    streams.sizes,
    streams.strides,
    streams.stream_count,
  )
  (remap, unique_vertex_count)
}

///|
pub fn generate_vertex_remap_multi_indexed(
  indices : FixedArray[UInt],
  streams : PackedStreams,
  vertex_count : Int,
) -> (FixedArray[UInt], Int) raise MeshoptError {
  require_packed_streams(streams, vertex_count)
  let remap = FixedArray::make(vertex_count, 0U)
  let unique_vertex_count = generate_vertex_remap_multi_indexed_ffi(
    remap,
    indices,
    indices.length(),
    streams.source,
    vertex_count,
    streams.offsets,
    streams.sizes,
    streams.strides,
    streams.stream_count,
  )
  (remap, unique_vertex_count)
}

///|
pub fn generate_shadow_index_buffer_multi(
  indices : FixedArray[UInt],
  streams : PackedStreams,
  vertex_count : Int,
) -> FixedArray[UInt] raise MeshoptError {
  require_packed_streams(streams, vertex_count)
  let destination = FixedArray::make(indices.length(), 0U)
  generate_shadow_index_buffer_multi_ffi(
    destination,
    indices,
    indices.length(),
    streams.source,
    vertex_count,
    streams.offsets,
    streams.sizes,
    streams.strides,
    streams.stream_count,
  )
  destination
}

///|
pub fn generate_vertex_remap_custom_no_callback(
  positions : FixedArray[Float],
  vertex_count : Int,
  positions_stride : Int,
) -> (FixedArray[UInt], Int) raise MeshoptError {
  require_float_stream(
    positions, vertex_count, positions_stride, 12, "positions",
  )
  let remap = FixedArray::make(vertex_count, 0U)
  let unique_vertex_count = generate_vertex_remap_custom_no_callback_ffi(
    remap, positions, vertex_count, positions_stride,
  )
  (remap, unique_vertex_count)
}

///|
pub fn generate_vertex_remap_custom(
  indices : FixedArray[UInt],
  positions : FixedArray[Float],
  vertex_count : Int,
  positions_stride : Int,
  callback : (UInt, UInt) -> Bool,
) -> (FixedArray[UInt], Int) raise MeshoptError {
  require_float_stream(
    positions, vertex_count, positions_stride, 12, "positions",
  )
  if indices.length() > 0 {
    let vertex_count_u = vertex_count.reinterpret_as_uint()
    for i in 0..