///|
/// A run of input characters encoded with one mode.
///
/// 使用同一模式编码的一段输入字符。
pub struct Segment {
  mode_ : Mode
  data_ : String
}

///|
pub fn Segment::new(mode : Mode, data : String) -> Segment {
  { mode_: mode, data_: data, }
}

///|
pub fn Segment::mode(self : Segment) -> Mode {
  self.mode_
}

///|
pub fn Segment::data(self : Segment) -> String {
  self.data_
}

///|
/// Value of an alphanumeric character (ISO Table 5), if it belongs to the set.
fn alnum_value(c : Char) -> Int32? {
  if c >= '0' && c <= '9' {
    Some(c.to_int() - '0'.to_int())
  } else if c >= 'A' && c <= 'Z' {
    Some(c.to_int() - 'A'.to_int() + 10)
  } else {
    match c {
      ' ' => Some(36)
      '$' => Some(37)
      '%' => Some(38)
      '*' => Some(39)
      '+' => Some(40)
      '-' => Some(41)
      '.' => Some(42)
      '/' => Some(43)
      ':' => Some(44)
      _ => None
    }
  }
}

///|
fn classify(c : Char) -> Mode {
  if c >= '0' && c <= '9' {
    Numeric
  } else if alnum_value(c) is Some(_) {
    Alphanumeric
  } else {
    Byte
  }
}

///|
fn validate(text : String, mode : Mode) -> Unit raise QrError {
  for c in text {
    let ok = match mode {
      Numeric => c >= '0' && c <= '9'
      Alphanumeric => alnum_value(c) is Some(_)
      Byte => true
    }
    if !ok {
      raise QrError::InvalidCharacter(char=c, mode~)
    }
  }
}

///|
/// Character count written into the segment header.
///
/// 写入段头的字符计数。
pub fn Segment::char_count(self : Segment) -> Int32 {
  match self.mode_ {
    Byte => @utf8.encode(self.data_).length()
    _ => self.data_.length()
  }
}

///|
/// Payload bit length, excluding mode and character-count fields.
///
/// 载荷比特长度,不含模式和字符计数字段。
pub fn Segment::data_bits(self : Segment) -> Int32 {
  match self.mode_ {
    Numeric => {
      let n = self.data_.length()
      let rem = n % 3
      n / 3 * 10 + (if rem == 2 { 7 } else if rem == 1 { 4 } else { 0 })
    }
    Alphanumeric => {
      let n = self.data_.length()
      n / 2 * 11 + (if n % 2 == 1 { 6 } else { 0 })
    }
    Byte => @utf8.encode(self.data_).length() * 8
  }
}

///|
/// Merges short non-byte runs into an adjacent byte run when doing so saves
/// header bits, then merges adjacent byte runs. Purely numeric/alphanumeric
/// inputs are left alone because they have no byte neighbour.
fn merge_short_runs(segments : Array[Segment]) -> Array[Segment] {
  let result : Array[Segment] = []
  let n = segments.length()
  for i in 0.. 0 && segments[i - 1].mode_ == Byte
    let next_byte = i + 1 < n && segments[i + 1].mode_ == Byte
    let has_byte_neighbor = prev_byte || next_byte
    let short = seg.data_.length()
    let numeric_short = seg.mode_ == Numeric && short <= 2
    let alpha_short = seg.mode_ == Alphanumeric && short <= 4
    let absorb = has_byte_neighbor && (numeric_short || alpha_short)
    let as_byte = if seg.mode_ == Byte || absorb {
      Some(Segment::new(Byte, seg.data_))
    } else {
      None
    }
    match as_byte {
      Some(b) =>
        if result.length() > 0 && result[result.length() - 1].mode_ == Byte {
          match result.pop() {
            Some(prev) => result.push(Segment::new(Byte, prev.data_ + b.data_))
            None => result.push(b)
          }
        } else {
          result.push(b)
        }
      None => result.push(seg)
    }
  }
  result
}

///|
/// Splits text into greedy class runs. Forcing `mode` produces a single segment
/// and rejects characters outside that mode's repertoire.
///
/// 将文本拆分为贪心分类段。强制指定 `mode` 时生成单个段,
/// 并拒绝该模式字符集之外的字符。
pub fn split_segments(
  text : String,
  mode? : Mode,
) -> Array[Segment] raise QrError {
  if text.is_empty() {
    raise QrError::EmptyInput
  }
  match mode {
    Some(m) => {
      validate(text, m)
      [Segment::new(m, text)]
    }
    None => {
      let segments : Array[Segment] = []
      let sb = StringBuilder()
      let mut current = Mode::Byte
      let mut has_content = false
      for c in text {
        let m = classify(c)
        if !has_content {
          current = m
          has_content = true
          sb.write_char(c)
        } else if m == current {
          sb.write_char(c)
        } else {
          segments.push(Segment::new(current, sb.to_string()))
          sb.reset()
          current = m
          sb.write_char(c)
        }
      }
      segments.push(Segment::new(current, sb.to_string()))
      merge_short_runs(segments)
    }
  }
}

///|
/// Mode indicator, ISO Table 4 (Kanji omitted in v0.x).
fn mode_indicator(mode : Mode) -> Int32 {
  match mode {
    Numeric => 1
    Alphanumeric => 2
    Byte => 4
  }
}

///|
/// Total bits needed for the segments at a given version, headers included.
///
/// 给定版本下所有段的总比特数,包含段头。
pub fn bits_needed(segments : Array[Segment], version : Int32) -> Int32 {
  let mut total = 0
  for seg in segments {
    total += 4 + char_count_bits(version, seg.mode_) + seg.data_bits()
  }
  total
}

///|
fn append_segment(buf : BitBuffer, seg : Segment, version : Int32) -> Unit {
  buf.push_bits(mode_indicator(seg.mode_), 4)
  buf.push_bits(seg.char_count(), char_count_bits(version, seg.mode_))
  match seg.mode_ {
    Numeric => {
      let digits : Array[Int32] = []
      for c in seg.data_ {
        digits.push(c.to_int() - '0'.to_int())
      }
      let mut i = 0
      while i < digits.length() {
        let remaining = digits.length() - i
        if remaining >= 3 {
          buf.push_bits(
            digits[i] * 100 + digits[i + 1] * 10 + digits[i + 2],
            10,
          )
          i += 3
        } else if remaining == 2 {
          buf.push_bits(digits[i] * 10 + digits[i + 1], 7)
          i += 2
        } else {
          buf.push_bits(digits[i], 4)
          i += 1
        }
      }
    }
    Alphanumeric => {
      let mut pending : Int32? = None
      for c in seg.data_ {
        if alnum_value(c) is Some(v) {
          match pending {
            None => pending = Some(v)
            Some(first) => {
              buf.push_bits(first * 45 + v, 11)
              pending = None
            }
          }
        }
      }
      if pending is Some(first) {
        buf.push_bits(first, 6)
      }
    }
    Byte =>
      for b in @utf8.encode(seg.data_) {
        buf.push_bits(b.to_int(), 8)
      }
  }
}