// euc.mbt
//
// EUC-JP 与 EUC-KR 的编解码,逐条实现 encoding.bs(whatwg/encoding @ 2c3853e)的
// §euc-jp-decoder、§euc-jp-encoder、§euc-kr-decoder、§euc-kr-encoder。
//
// 与前面编码的差异(都按规范,不可凭记忆合并):
// - EUC-JP:SS2(0x8E + 半角片假名单字节)、SS3(0x8F + jis0212 三字节序列,
//   jis0212 标志在通用分支里**无论是否命中都复位**);双字节走共享 jis0208
//   (同一指针空间,94 列布局,见 gen_big5_sjis.mbt 注释);
//   编码端与 Shift_JIS 同款特例(¥→0x5C、U+203E→0x7E、U+2212→U+FF0D),
//   半角片假名编码为两字节 0x8E + (cp-0xFF61+0xA1);
//   新鲜字节 0x80/0xA0/0xFF 报错——**与 Shift_JIS 的 0x80→U+0080 不同**。
// - EUC-KR:单 lead、尾字节区间 0x41..0xFE 用**单一偏移 0x41**(没有 GBK 的
//   0x7F/0x80 分界),编码端无任何特例;新鲜字节 0x80/0xA0/0xFF 报错——
//   **与 GBK 的 0x80→U+20AC 不同**。

///|
/// EUC-JP 状态:lead + SS3 的 jis0212 标志(§euc-jp-decoder)。
priv struct EucJpState {
  mut lead : Int
  mut jis0212 : Bool
}

///|
fn EucJpState::new() -> EucJpState {
  { lead: 0, jis0212: false, }
}

///|
fn EucJpState::has_pending(self : EucJpState) -> Bool {
  self.lead != 0
}

///|
fn EucJpState::reset(self : EucJpState) -> Unit {
  self.lead = 0
  self.jis0212 = false
}

///|
/// EUC-JP 解码一个字节块(§euc-jp-decoder 逐步翻译)。
fn euc_jp_consume(state : EucJpState, input : Bytes) -> String {
  let bytes = input.to_array()
  let out : Array[Char] = []
  let mut replay : Array[Byte] = []
  let mut r = 0
  let mut i = 0
  while i < bytes.length() || r < replay.length() {
    let mut b = -1
    if r < replay.length() {
      b = replay[r].to_int()
      r += 1
    } else {
      b = bytes[i].to_int()
      i += 1
    }
    if state.lead == 0x8E && b >= 0xA1 && b <= 0xDF {
      // SS2:0x8E + 半角片假名单字节 → U+FF61..U+FF9F
      state.lead = 0
      out.push(int_to_char(0xFF61 - 0xA1 + b))
    } else if state.lead == 0x8F && b >= 0xA1 && b <= 0xFE {
      // SS3:置 jis0212 标志,第二个字节转为 lead,等第三个字节
      state.jis0212 = true
      state.lead = b
    } else if state.lead != 0 {
      let lead = state.lead
      state.lead = 0
      let mut cp = -1
      if lead >= 0xA1 && lead <= 0xFE && b >= 0xA1 && b <= 0xFE {
        let pointer = (lead - 0xA1) * 94 + (b - 0xA1)
        let table = if state.jis0212 { jis0212_index } else { jis0208_index }
        if pointer < table.length() {
          cp = table[pointer]
        }
      }
      // 规范:无论是否命中,jis0212 标志都要复位
      state.jis0212 = false
      if cp >= 0 {
        out.push(int_to_char(cp))
      } else {
        if b < 0x80 {
          let restored : Array[Int] = [b]
          replay = replay_prepend(replay, r, restored)
          r = 0
        }
        out.push(replacement_char())
      }
    } else if b < 0x80 {
      out.push(int_to_char(b))
    } else if b == 0x8E || b == 0x8F || (b >= 0xA1 && b <= 0xFE) {
      state.lead = b
    } else {
      out.push(replacement_char()) // 0x80 / 0xA0 / 0xFF 等
    }
  }
  String::from_iter(out.iter())
}

///|
/// EUC-JP 流结束:有挂起 lead → 单个 U+FFFD(§euc-jp-decoder end-of-queue)。
fn euc_jp_finish(state : EucJpState) -> String {
  if state.has_pending() {
    state.reset()
    let out : Array[Char] = [replacement_char()]
    return String::from_iter(out.iter())
  }
  ""
}

///|
/// EUC-JP 编码(§euc-jp-encoder:ASCII、¥、overbar、半角片假名两字节 SS2、
/// U+2212→U+FF0D,再走共享 jis0208 的首次出现反向表)。
fn euc_jp_encode(text : String) -> Result[Bytes, EncodingError] {
  let out : Array[Byte] = []
  let mut index = 0
  for c in text {
    let mut cp = c.to_int()
    if cp < 0x80 {
      out.push(int_to_byte(cp))
    } else if cp == 0x00A5 {
      out.push(int_to_byte(0x5C))
    } else if cp == 0x203E {
      out.push(int_to_byte(0x7E))
    } else if cp >= 0xFF61 && cp <= 0xFF9F {
      out.push(int_to_byte(0x8E))
      out.push(int_to_byte(cp - 0xFF61 + 0xA1))
    } else {
      if cp == 0x2212 {
        cp = 0xFF0D // 规范:归一后再查索引,错误也报归一后的码点
      }
      let pointer = encode_index_lookup(euc_jp_encode_index, cp)
      if pointer < 0 {
        return Err(EncodingError::Unmappable(cp, index))
      }
      out.push(int_to_byte(pointer / 94 + 0xA1))
      out.push(int_to_byte(pointer % 94 + 0xA1))
    }
    index += 1
  }
  Ok(Bytes::from_array(out.exact_view()))
}

///|
/// EUC-KR 解码一个字节块(§euc-kr-decoder:尾字节 0x41..0xFE 单偏移 0x41)。
fn euc_kr_consume(state : LeadState, input : Bytes) -> String {
  let bytes = input.to_array()
  let out : Array[Char] = []
  let mut replay : Array[Byte] = []
  let mut r = 0
  let mut i = 0
  while i < bytes.length() || r < replay.length() {
    let mut b = -1
    if r < replay.length() {
      b = replay[r].to_int()
      r += 1
    } else {
      b = bytes[i].to_int()
      i += 1
    }
    if state.lead != 0 {
      let lead = state.lead
      state.lead = 0
      let mut cp = -1
      if b >= 0x41 && b <= 0xFE {
        let pointer = (lead - 0x81) * 190 + (b - 0x41)
        if pointer < euc_kr_index.length() {
          cp = euc_kr_index[pointer]
        }
      }
      if cp >= 0 {
        out.push(int_to_char(cp))
      } else {
        if b < 0x80 {
          let restored : Array[Int] = [b]
          replay = replay_prepend(replay, r, restored)
          r = 0
        }
        out.push(replacement_char())
      }
    } else if b < 0x80 {
      out.push(int_to_char(b))
    } else if b >= 0x81 && b <= 0xFE {
      state.lead = b
    } else {
      out.push(replacement_char()) // 0x80 / 0xA0 / 0xFF 等(与 GBK 的 0x80→U+20AC 不同)
    }
  }
  String::from_iter(out.iter())
}

///|
/// EUC-KR 流结束:有挂起 lead → 单个 U+FFFD。
fn euc_kr_finish(state : LeadState) -> String {
  if state.lead != 0 {
    state.lead = 0
    let out : Array[Char] = [replacement_char()]
    return String::from_iter(out.iter())
  }
  ""
}

///|
/// EUC-KR 编码(§euc-kr-encoder:ASCII 直通,其余查首次出现反向表,
/// lead = ptr/190 + 0x81、trail = ptr%190 + 0x41)。
fn euc_kr_encode(text : String) -> Result[Bytes, EncodingError] {
  let out : Array[Byte] = []
  let mut index = 0
  for c in text {
    let cp = c.to_int()
    if cp < 0x80 {
      out.push(int_to_byte(cp))
    } else {
      let pointer = encode_index_lookup(euc_kr_encode_index, cp)
      if pointer < 0 {
        return Err(EncodingError::Unmappable(cp, index))
      }
      out.push(int_to_byte(pointer / 190 + 0x81))
      out.push(int_to_byte(pointer % 190 + 0x41))
    }
    index += 1
  }
  Ok(Bytes::from_array(out.exact_view()))
}