///|
let media_type : String = "application/vnd.oasis.opendocument.spreadsheet"

///|
fn manifest_xml() -> String {
  ""
}

///|
fn styles_xml() -> String {
  "\{preset_styles()}"
}

// ODF needs mimetype first; zipc Archive sorts paths (META-INF sorts first).
// This small ZIP32 STORED envelope uses zipc's tested File CRC implementation.
// Fixed ASCII paths only, no assets or path input; no timestamps/extras/comments.

///|
fn stored_package(files : Array[(String, Bytes)]) -> Bytes raise OdsError {
  let body = @buffer.Buffer()
  let central = @buffer.Buffer()
  for (path, data) in files {
    let name = @utf8.encode(path[:])
    let offset = body.length()
    let size = data.length()
    if size > 33554432 || offset + size > 33554432 {
      raise Invalid("package exceeds 32 MiB")
    }
    let crc = @zip.File::stored_from_bytes(data).decompressed_crc32()
    body.write_int_le(0x04034b50)
    for n in [20, 0, 0, 0, 33] {
      body.write_uint16_le(n.to_uint16())
    }
    for n in [crc, size, size] {
      body.write_int_le(n)
    }
    body.write_uint16_le(name.length().to_uint16())
    body.write_uint16_le(0)
    body.write_bytes(name[:])
    body.write_bytes(data[:])
    central.write_int_le(0x02014b50)
    for n in [20, 20, 0, 0, 0, 33] {
      central.write_uint16_le(n.to_uint16())
    }
    for n in [crc, size, size] {
      central.write_int_le(n)
    }
    for n in [name.length(), 0, 0, 0, 0] {
      central.write_uint16_le(n.to_uint16())
    }
    central.write_int_le(0)
    central.write_int_le(offset)
    central.write_bytes(name[:])
  }
  let cd_offset = body.length()
  let cd_size = central.length()
  body.write_bytes(central.to_bytes()[:])
  body.write_int_le(0x06054b50)
  for n in [0, 0, files.length(), files.length()] {
    body.write_uint16_le(n.to_uint16())
  }
  body.write_int_le(cd_size)
  body.write_int_le(cd_offset)
  body.write_uint16_le(0)
  if body.length() > 33554432 {
    raise Invalid("package exceeds 32 MiB")
  }
  body.to_bytes()
}

///|
/// Return a complete deterministic ODS package. I/O belongs to the caller.
pub fn Workbook::to_ods(self : Workbook) -> Bytes raise OdsError {
  stored_package([
    ("mimetype", @utf8.encode(media_type[:])),
    ("content.xml", @utf8.encode(self.content_xml()[:])),
    ("styles.xml", @utf8.encode(styles_xml()[:])),
    ("META-INF/manifest.xml", @utf8.encode(manifest_xml()[:])),
  ])
}