// ASCII and padding rules adapted from datamatrix-rs encodation (MIT).

///|
fn ascii_codewords(data : Bytes) -> Array[Int] {
  let out = []
  let mut i = 0
  while i < data.length() {
    let a = data[i].to_int()
    if i + 1 < data.length() &&
      a >= 48 &&
      a <= 57 &&
      data[i + 1] >= b'0' &&
      data[i + 1] <= b'9' {
      out.push(130 + (a - 48) * 10 + data[i + 1].to_int() - 48)
      i += 2
    } else {
      if a >= 128 {
        out.push(235)
        out.push(a - 127)
      } else {
        out.push(a + 1)
      }
      i += 1
    }
  }
  out
}

///|
fn pad_data(
  codewords : Array[Int],
  options : EncodeOptions,
) -> Size raise EncodeError {
  let s = select_size(codewords.length(), options)
  if codewords.length() < s.capacity {
    codewords.push(129)
  }
  while codewords.length() < s.capacity {
    let value = 129 + 149 * (codewords.length() + 1) % 253 + 1
    codewords.push(if value > 254 { value - 254 } else { value })
  }
  s
}

///|
fn finish(
  codewords : Array[Int],
  options : EncodeOptions,
) -> Symbol raise EncodeError {
  let s = pad_data(codewords, options)
  let ecc = error_code(codewords, s)
  for b in ecc {
    codewords.push(b)
  }
  place(codewords, s)
}

///|
/// Encode raw bytes without ECI or any transformation.
pub fn encode_bytes(
  data : Bytes,
  options? : EncodeOptions = { shape: Square, },
) -> Symbol raise EncodeError {
  if data.length() > 3116 {
    raise CapacityExceeded
  }
  finish(planned_codewords(data, false, options), options)
}

///|
/// Encode UTF-8 text with ECI 26, preserving all whitespace and zero bytes.
pub fn encode_text(
  text : String,
  options? : EncodeOptions = { shape: Square, },
) -> Symbol raise EncodeError {
  let data = @utf8.encode(text)
  if data.length() > 3116 {
    raise CapacityExceeded
  }
  finish(planned_codewords(data, true, options), options)
}