// DXF 的 ASCII 解析:把字节流切成 tag 列表。
//
// ASCII DXF 的正文是**一行组码、一行值**交替排列:
//
//       0          ← 组码按 3 位右对齐(这是规范里的 %3d 格式)
//     CIRCLE       ← 值不缩进
//      10
//     0.0
//
// 两个真实世界才暴露得出来的细节:
//
//   1. **组码行有前导空白,值行没有。** 实测 `circle.dxf` 有 1124/3016 行
//      带前导空白,`Minimal_DXF_AC1021.dxf` 一行都没有。所以组码必须 trim。
//   2. **值不能跟着 trim。** TEXT 实体的文字内容可能真的以空格开头,
//      一起 trim 掉就把用户的数据改了。数值类型的值当然可以 trim
//      (数字周围的空格无意义),字符串类型的必须原样留着。
//
// 换行三种都要认:`\n`、`\r\n`(Windows 导出的基本都这样)、以及裸 `\r`
// (老 Mac 的写法,在古董图纸里真的会遇到)。

///|
/// 一次扫描的结果。
///
/// 警告而不是异常:DXF 大量来自第三方导出器,坏文件比想象中多,
/// 「读了 90% 然后告诉你哪里坏了」比「抛异常什么都没读出来」有用得多。
pub(all) struct TagScan {
  tags : Array[Tag]
  warnings : Array[String]
  /// 文件用的编码(从 HEADER 判断出来的)
  encoding : Encoding
}

///|
/// 把 ASCII DXF 的字节流切成 tag。
///
/// 碰到 `EOF` 就停——真实的 DXF 文件在 `EOF` 之后还可能有垃圾字节
/// (实测 ezdxf 的语料里就有 `R12_with_trash_beyond_EOF.dxf`),
/// 那些不是数据,继续解会把噪声当成 tag。
///
/// 这是**整份扫描**的入口;分块扫描走 `stream.mbt` 的 `TagStream`。
/// 两个入口共用同一套状态机(这里就是它的一次性封装),
/// 行为不会有两套。
pub fn scan_tags(data : Bytes) -> TagScan {
  let stream = TagStream::new()
  let tags = stream.feed(data)
  tags.append(stream.finish())
  { tags, warnings: stream.warnings(), encoding: stream.encoding().unwrap(), }
}

///|
/// 从 `from` 开始找这一行的结束位置(不含换行符本身)。
fn line_end(data : Bytes, from : Int) -> Int {
  let mut i = from
  while i < data.length() && data[i] != b'\n' && data[i] != b'\r' {
    i = i + 1
  }
  i
}

///|
/// 从行尾跳到下一行的开头,三种换行都认。
fn advance_past_newline(data : Bytes, at : Int) -> Int {
  let mut i = at
  if i < data.length() && data[i] == b'\r' {
    i = i + 1
  }
  if i < data.length() && data[i] == b'\n' {
    i = i + 1
  }
  i
}

///|
/// 这一行是不是只有空白。
fn line_is_blank(data : Bytes, from : Int, to : Int) -> Bool {
  for i = from; i < to; i = i + 1 {
    let c = data[i].to_int() & 0xFF
    if c != 0x20 && c != 0x09 {
      return false
    }
  }
  true
}

///|
/// 组码那一行的内容摘录,出错信息里用。太长的截断。
fn preview(data : Bytes, from : Int, to : Int) -> String {
  let sb = StringBuilder()
  let stop = if to - from > 40 { from + 40 } else { to }
  for i = from; i < stop; i = i + 1 {
    let c = data[i].to_int() & 0xFF
    if c >= 0x20 && c < 0x7F {
      sb.write_char(code_point_to_char(c))
    } else {
      sb.write_char('?')
    }
  }
  sb.to_string()
}

///|
/// 解析组码。
///
/// **必须 trim**:组码按 3 位右对齐(`  0`、` 70`、`100`),不 trim 的话
/// 除了三位数以外全都解析不出来。实测 `circle.dxf` 有一千多行是缩进的。
fn parse_group_code(data : Bytes, from : Int, to : Int) -> Int? {
  let (start, stop) = trim_ascii_range(data, from, to)
  if start >= stop {
    return None
  }
  // 逐位算,不转字符串——组码就在这几行里出现几十万次
  let mut value = 0
  let mut negative = false
  let mut i = start
  if data[i] == b'-' {
    negative = true
    i = i + 1
  }
  if i >= stop {
    return None
  }
  while i < stop {
    let c = data[i].to_int() & 0xFF
    if c < 0x30 || c > 0x39 {
      return None
    }
    if value > 214748364 || (value == 214748364 && c - 0x30 > 7) {
      return None
    }
    value = value * 10 + (c - 0x30)
    i = i + 1
  }
  if negative {
    Some(-value)
  } else {
    Some(value)
  }
}

///|
/// 去掉一段字节两侧的空格和制表符。返回新的 `[start, stop)`。
fn trim_ascii_range(data : Bytes, from : Int, to : Int) -> (Int, Int) {
  let mut start = from
  let mut stop = to
  while start < stop {
    let c = data[start].to_int() & 0xFF
    if c == 0x20 || c == 0x09 {
      start = start + 1
    } else {
      break
    }
  }
  while stop > start {
    let c = data[stop - 1].to_int() & 0xFF
    if c == 0x20 || c == 0x09 {
      stop = stop - 1
    } else {
      break
    }
  }
  (start, stop)
}

///|
/// 解析一个值。
///
/// **字符串类型不 trim**,数值类型 trim——理由见文件头。
fn parse_value(
  code : Int,
  data : Bytes,
  from : Int,
  to : Int,
  encoding : Encoding,
) -> Result[TagValue, String] {
  let kind = code_type(code)
  if kind is Str || kind is Bin {
    return Ok(Str(decode_text(slice_range(data, from, to), encoding)))
  }
  let (start, stop) = trim_ascii_range(data, from, to)
  let text = bytes_to_ascii(data, start, stop)
  match kind {
    F64 => {
      let value = @string.parse_double(text[:]) catch {
        _ => return Err("不是有效浮点数")
      }
      if !finite_number(value) {
        return Err("数值必须有限")
      }
      Ok(F64(value))
    }
    I16 | I32 => {
      let value = @string.parse_int(text[:]) catch {
        _ => return Err("不是有效整数或超出32位范围")
      }
      if kind is I16 && (value < -32768 || value > 65535) {
        return Err("数值超出16位范围")
      }
      Ok(if kind is I16 { I16(value) } else { I32(value) })
    }
    I64 => {
      let value = @string.parse_int64(text[:]) catch {
        _ => return Err("不是有效64位整数")
      }
      Ok(I64(value))
    }
    Bool =>
      if text == "0" {
        Ok(Bool(false))
      } else if text == "1" {
        Ok(Bool(true))
      } else {
        Err("布尔数值必须为0或1")
      }
    Str | Bin => Ok(Str(text))
  }
}

///|
/// 把一段字节复制出来。
fn slice_range(data : Bytes, from : Int, to : Int) -> Bytes {
  let out : Array[Byte] = []
  for i = from; i < to; i = i + 1 {
    out.push(data[i])
  }
  Bytes::from_array(out)
}