///|
/// Code 39 字符集(不含起始/终止符 `*`),下标即 mod-43 校验值。
let code39_charset : String = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%"

///|
/// Code 39 字符到 9 位模式的映射(1=宽元素,0=窄元素;元素按 条-空-条-空 交替)。
let code39_patterns : Array[String] = [
  "000110100", // 0
   "100100001", // 1
   "001100001", // 2
   "101100000", // 3
   "000110001", // 4
   "100110000", // 5
   "001110000", // 6
   "000100101", // 7
   "100100100", // 8
   "001100100", // 9
   "100001001", // A
   "001001001", // B
   "101001000", // C
   "000011001", // D
   "100011000", // E
   "001011000", // F
   "000001101", // G
   "100001100", // H
   "001001100", // I
   "000011100", // J
   "100000011", // K
   "001000011", // L
   "101000010", // M
   "000010011", // N
   "100010010", // O
   "001010010", // P
   "000000111", // Q
   "100000110", // R
   "001000110", // S
   "000010110", // T
   "110000001", // U
   "011000001", // V
   "111000000", // W
   "010010001", // X
   "110010000", // Y
   "011010000", // Z
   "010000101", // -
   "110000100", // .
   "011000100", // space
   "010101000", // $
   "010100010", // /
   "010001010", // +
   "000101010", // %
]

///|
/// 起始/终止符 `*` 的模式。
let code39_star : String = "010010100"

///|
/// 返回字符在 Code 39 字符集中的下标(即 mod-43 校验值),`*` 不可作为数据。
fn code39_value(c : Byte) -> Int? {
  let cs = @utf8.encode(code39_charset)
  for i = 0; i < cs.length(); i = i + 1 {
    if cs[i] == c {
      return Some(i)
    }
  }
  None
}

///|
/// 计算 Code 39 mod-43 校验值(数据不含起始/终止符)。
pub fn code39_checksum(data : String) -> Result[Int, String] {
  let b = @utf8.encode(data)
  if b.length() == 0 {
    return Err("Code 39 data must not be empty")
  }
  let mut sum = 0
  for i = 0; i < b.length(); i = i + 1 {
    match code39_value(b[i]) {
      None => return Err("Code 39 data contains unsupported character")
      Some(v) => sum = sum + v
    }
  }
  Ok(sum % 43)
}

///|
/// 编码 Code 39。
///
/// `data` 使用 43 字符基本字符集(`*` 除外);`with_checksum` 为 true 时自动
/// 追加 mod-43 校验字符。输出模块数 = `16 * N + 31`(N 为数据字符数)。
pub fn encode_code39(
  data : String,
  with_checksum? : Bool = false,
) -> Result[Barcode, EncodeError] {
  let b = @utf8.encode(data)
  if b.length() == 0 {
    return Err(InvalidData("Code 39 data must not be empty"))
  }
  let values : Array[Int] = []
  for i = 0; i < b.length(); i = i + 1 {
    match code39_value(b[i]) {
      None =>
        return Err(
          InvalidData(
            "Code 39 data contains unsupported character at position \{i}",
          ),
        )
      Some(v) => values.push(v)
    }
  }
  let mut payload = data
  if with_checksum {
    match code39_checksum(data) {
      Err(msg) => return Err(InvalidData(msg))
      Ok(check) => {
        let cs = @utf8.encode(code39_charset)
        payload = data + cs[check].to_char().to_string()
        values.push(check)
      }
    }
  }
  let modules : Array[Bool] = []
  append_code39_char(modules, code39_star)
  for v in values {
    modules.push(false) // 字符间窄空
    append_code39_char(modules, code39_patterns[v])
  }
  modules.push(false)
  append_code39_char(modules, code39_star)
  Ok({ symbology: Code39, data: payload, modules })
}

///|
/// 将一个 Code 39 字符模式展开为模块:宽元素 3 模块,窄元素 1 模块。
fn append_code39_char(modules : Array[Bool], pattern : String) -> Unit {
  let p = @utf8.encode(pattern)
  for i = 0; i < p.length(); i = i + 1 {
    let is_bar = i % 2 == 0
    let width = if p[i] == b'1' { 3 } else { 1 }
    for _ in 0.. Result[Unit, EncodeError] {
  match encode_code39(data) {
    Err(e) => Err(e)
    Ok(_) => Ok(())
  }
}