// Zlib format support - deflate with headers and checksums
// RFC 1950: ZLIB Compressed Data Format Specification

// Zlib compressed data structure

///|
pub struct ZlibData {
  compressed_bytes : Bytes
  original_size : Int
  adler32_checksum : @adler32.Adler32
}

// Compress data using zlib format (deflate + headers + checksum)

///|
pub fn zlib_compress_bytes(data : Bytes, level : Level) -> ZlibData {
  // Calculate Adler-32 of original data
  let adler = @adler32.bytes(data)

  // Compress using deflate
  let deflate_data = deflate_compress(data, level)
  // Create zlib header (2 bytes)
  let compression_method = 8 // Deflate
  let compression_info = 7 // 32K window size
  let cmf = compression_method | (compression_info << 4)
  let fcheck = 0 // Will be calculated
  let fdict = 0 // No preset dictionary
  let flevel = match level {
    Level::None => 0 // Fastest
    Level::Fast => 1 // Fast
    Level::Default => 2 // Default
    Level::Best => 3 // Maximum compression
  }
  let flg = (flevel << 6) | (fdict << 5) | fcheck

  // Calculate FCHECK to make (CMF*256 + FLG) divisible by 31
  let header_base = cmf * 256 + flg
  let fcheck_calculated = (31 - header_base % 31) % 31
  let flg_final = (flg & 0xe0) | fcheck_calculated

  // Build zlib data: header + deflate data + adler32
  let header = write_u16_be_bytes(cmf * 256 + flg_final)
  let adler_bytes = write_u32_be_bytes(adler.0.to_int())
  let zlib_bytes = header + deflate_data.compressed_bytes + adler_bytes
  {
    compressed_bytes: zlib_bytes,
    original_size: data.length(),
    adler32_checksum: adler,
  }
}

// Decompress zlib data

///|
pub fn zlib_decompress_bytes(zlib_data : ZlibData) -> Bytes raise {
  let data = zlib_data.compressed_bytes
  if data.length() < 6 {
    fail("Zlib data too short (minimum 6 bytes)")
  }

  // Parse zlib header (2 bytes)
  let cmf = data[0].to_int()
  let flg = data[1].to_int()

  // Verify header checksum
  let header_check = (cmf * 256 + flg) % 31
  if header_check != 0 {
    fail("Invalid zlib header checksum")
  }

  // Extract compression method and info
  let compression_method = cmf & 0x0f
  let compression_info = (cmf >> 4) & 0x0f
  if compression_method != 8 {
    fail("Unsupported compression method: " + compression_method.to_string())
  }
  if compression_info > 7 {
    fail("Invalid compression info: " + compression_info.to_string())
  }

  // Check for preset dictionary (not supported)
  let fdict = (flg >> 5) & 1
  if fdict != 0 {
    fail("Preset dictionaries not supported")
  }

  // Extract deflate data (everything except header and trailing checksum)
  let deflate_start = 2
  let deflate_end = data.length() - 4
  let deflate_bytes = data[deflate_start:deflate_end].to_bytes()

  // Extract Adler-32 checksum (last 4 bytes, big-endian)
  let expected_adler = read_u32_be_bytes(data, deflate_end).to_int64()

  // Decompress deflate data
  let deflate_data : DeflateData = {
    compressed_bytes: deflate_bytes,
    original_size: zlib_data.original_size,
    adler32_checksum: expected_adler,
  }
  let decompressed = deflate_decompress(deflate_data)
  // Verify Adler-32 checksum
  let computed_adler = @adler32.bytes(decompressed)
  if computed_adler == expected_adler {
    decompressed
  } else {
    fail("Adler-32 checksum mismatch in zlib data")
  }
}

// Helper function to write 16-bit big-endian integer as bytes

///|
fn write_u16_be_bytes(value : Int) -> Bytes {
  let b0 = (value >> 8) & 0xff
  let b1 = value & 0xff
  Bytes::from_array([b0.to_byte(), b1.to_byte()])
}

// Helper function to write 32-bit big-endian integer as bytes

///|
fn write_u32_be_bytes(value : Int) -> Bytes {
  let b0 = (value >> 24) & 0xff
  let b1 = (value >> 16) & 0xff
  let b2 = (value >> 8) & 0xff
  let b3 = value & 0xff
  Bytes::from_array([b0.to_byte(), b1.to_byte(), b2.to_byte(), b3.to_byte()])
}

// Helper function to read 32-bit big-endian integer from bytes

///|
fn read_u32_be_bytes(data : Bytes, offset : Int) -> Int {
  if offset + 3 >= data.length() {
    return 0
  }
  let b0 = data[offset].to_int()
  let b1 = data[offset + 1].to_int()
  let b2 = data[offset + 2].to_int()
  let b3 = data[offset + 3].to_int()
  (b0 << 24) + (b1 << 16) + (b2 << 8) + b3
}

// Bytes-based zlib functions (more efficient for binary data)

///|
pub fn zlib_of_bytes(data : Bytes, level : Level) -> Bytes {
  // Use the modern bytes-based implementation directly
  let zlib_data = zlib_compress_bytes(data, level)
  zlib_data.compressed_bytes
}

///|
pub fn zlib_to_bytes(
  compressed_data : Bytes,
  original_size : Int,
) -> Bytes raise {
  // Use the modern bytes-based implementation directly
  let zlib_data : ZlibData = {
    compressed_bytes: compressed_data,
    original_size,
    adler32_checksum: 0L, // Will be read from data
  }
  zlib_decompress_bytes(zlib_data)
}