// 结构层:把 tag 流切成段 → 记录 → 子类。
//
// DXF 的正文分层:
//
//     SECTION(段)      HEADER / CLASSES / TABLES / BLOCKS / ENTITIES / OBJECTS
//       记录(组码 0)    CIRCLE / LAYER / BLOCK / …
//         子类(组码 100)AcDbEntity / AcDbCircle / …
//
// 第三层是 DXF 的特点:**组码的含义随子类变**。同一个组码 70,在
// `AcDbSymbolTable` 里是「最大条目数」,在图层表记录里是「标志位」。
// 所以「70 是什么意思」只有先知道当前在哪个子类里才答得上来:
//
//    0    CIRCLE
//    5    2D          ← 第一个 100 之前的公共字段(句柄)
//    330  17          ← 所有者引用
//    100  AcDbEntity  ← 子类从这里开始
//    8    0           ← 图层(AcDbEntity 的字段)
//    100  AcDbCircle  ← 换到下一个子类
//    10/20/30  圆心    ← AcDbCircle 的字段
//    40   半径
//
// 这一层只负责把结构切出来,**不解释任何字段的含义**——那是实体层的事。

// ── 数据模型 ────────────────────────────────────────────────────────────

///|
/// 记录里的一段:由一个 `100` 子类标记开头的那些 tag。
///
/// 老版本(R10/R12)没有 `100` 标记,整条记录算作一个子类,
/// 名字用记录类型名(见 `subclass.mbt` 的合成逻辑)。
pub(all) struct Subclass {
  name : String
  tags : Array[Tag]
}

///|
/// 一条记录:组码 0 开头的一串 tag。
pub(all) struct Record {
  /// 记录类型名,就是组码 0 的值(`CIRCLE` / `LAYER` / `SECTION`…)
  kind : String
  /// 第一个 `100` 之前的公共 tag(组码 0 那条也在里面)
  common : Array[Tag]
  /// 各子类的 tag,按在文件里出现的顺序
  subclasses : Array[Subclass]
}

///|
/// 一个段。
pub(all) struct Section {
  /// 段名:`HEADER` / `CLASSES` / `TABLES` / `BLOCKS` / `ENTITIES` / `OBJECTS`
  name : String
  /// 段里的记录。
  ///
  /// **HEADER 段是特例**:它的变量之间没有组码 0 分隔,所以整段就是一条
  /// `SECTION` 记录,变量成对挂在它的 tag 里(`9` 是变量名,下一个 tag 是值)。
  /// 见 `header.mbt`。
  records : Array[Record]
}

///|
/// 解析出来的图纸骨架:只有结构,没有语义。
pub(all) struct Drawing {
  /// `$ACADVER` 的值(`AC1027` 之类)。没有 HEADER 段时是空串
  version : String
  sections : Array[Section]
  warnings : Array[String]
}

// ── 取字段 ──────────────────────────────────────────────────────────────

///|
/// 在记录里找一个组码(公共部分 + 所有子类,按顺序)。
///
/// **只看组码、不看子类**——需要区分同名组码时用 `get_in`。
/// 这个函数适合那些含义到处都一样的组码(图层 8、线型 6 之类)。
pub fn Record::get(self : Record, code : Int) -> Tag? {
  for t in self.common {
    if t.code == code {
      return Some(t)
    }
  }
  for sc in self.subclasses {
    for t in sc.tags {
      if t.code == code {
        return Some(t)
      }
    }
  }
  None
}

///|
/// 在指定的子类里找组码。
///
/// 这是「组码含义随子类变」的正面用法:明确说出我要的是哪个类的字段。
pub fn Record::get_in(self : Record, subclass : String, code : Int) -> Tag? {
  for sc in self.subclasses {
    if sc.name == subclass {
      for t in sc.tags {
        if t.code == code {
          return Some(t)
        }
      }
    }
  }
  None
}

///|
/// 取一个组码的字符串值。没有就是空串。
pub fn Record::get_string(self : Record, code : Int) -> String {
  match self.get(code) {
    Some(t) => t.value.as_string()
    None => ""
  }
}

///|
/// 取一个组码的浮点值。没有就用给的默认值。
///
/// 默认值由调用方给而不是固定 0.0:坐标缺省成 0 是合理的,
/// 但半径缺省成 0 就变成了「一个退化的圆」,性质完全不同。
pub fn Record::get_double(
  self : Record,
  code : Int,
  default : Double,
) -> Double {
  match self.get(code) {
    Some(t) => t.value.as_double()
    None => default
  }
}

///|
/// 取一个组码的整数值。
pub fn Record::get_int(self : Record, code : Int, default : Int) -> Int {
  match self.get(code) {
    Some(t) => t.value.as_int()
    None => default
  }
}

///|
/// 一个组码在记录里出现了几次。
///
/// LWPOLYLINE 的顶点就是同一组码重复出现(每个顶点一对 10/20),
/// 靠这个数出有几个顶点。
pub fn Record::count_code(self : Record, code : Int) -> Int {
  let mut n = 0
  for t in self.common {
    if t.code == code {
      n = n + 1
    }
  }
  for sc in self.subclasses {
    for t in sc.tags {
      if t.code == code {
        n = n + 1
      }
    }
  }
  n
}

///|
/// 某个组码出现的所有 tag,按顺序。
pub fn Record::all_of(self : Record, code : Int) -> Array[Tag] {
  let out : Array[Tag] = []
  for t in self.common {
    if t.code == code {
      out.push(t)
    }
  }
  for sc in self.subclasses {
    for t in sc.tags {
      if t.code == code {
        out.push(t)
      }
    }
  }
  out
}

// ── 切分 ────────────────────────────────────────────────────────────────

///|
/// 把 tag 流切成图纸骨架。
///
/// **绝不 panic**:坏结构变成警告。「读了 90% 然后告诉你哪里坏了」
/// 比「抛异常什么都没读出来」有用得多——真实世界的 DXF 大量来自第三方
/// 导出器,损坏的文件比想象中多。
pub fn build_drawing(scan : TagScan) -> Drawing {
  let records = split_records(scan.tags)
  let warnings = scan.warnings.copy()
  let sections = group_sections(records, warnings)
  let version = match find_header_var(sections, "$ACADVER") {
    Some(v) => v
    None => ""
  }
  { version, sections, warnings, }
}

///|
/// 把 tag 流切成记录。
///
/// 收尾的逻辑在循环里出现了两次(每次遇到新的 `0`,以及循环结束),
/// 所以抽成 `finish_record` 那种函数——但 MoonBit 的闭包捕获不了可变变量,
/// 硬抽反而更绕。这里选择**显式重复一遍**,两处加起来十来行,
/// 比绕一圈的写法好读。
fn split_records(tags : Array[Tag]) -> Array[Record] {
  let out : Array[Record] = []
  let mut kind : String? = None
  let mut common : Array[Tag] = []
  let mut subs : Array[Subclass] = []
  let mut sub_name : String? = None
  let mut sub_tags : Array[Tag] = []

  for tag in tags {
    if tag.is_record_start() {
      // ── 收尾上一条记录 ──
      match kind {
        Some(k) => {
          let finished = subs.copy()
          match sub_name {
            Some(n) => finished.push({ name: n, tags: sub_tags, })
            None => ()
          }
          out.push({ kind: k, common, subclasses: finished, })
        }
        None => ()
      }
      // ── 开一条新的 ──
      kind = Some(tag.value.as_string())
      common = [tag]
      subs = []
      sub_name = None
      sub_tags = []
    } else if tag.is_subclass_marker() {
      // 换子类:先把当前这个收掉
      match sub_name {
        Some(n) => subs.push({ name: n, tags: sub_tags, })
        None => ()
      }
      sub_name = Some(tag.value.as_string())
      sub_tags = []
    } else if sub_name is Some(_) {
      sub_tags.push(tag)
    } else {
      common.push(tag)
    }
  }

  // ── 最后一条 ──
  match kind {
    Some(k) => {
      let finished = subs.copy()
      match sub_name {
        Some(n) => finished.push({ name: n, tags: sub_tags, })
        None => ()
      }
      out.push({ kind: k, common, subclasses: finished, })
    }
    None => ()
  }
  out
}

///|
/// 把记录按 `SECTION` / `ENDSEC` 分成段。
///
/// `SECTION` 那一条记录里带着段名(组码 2),但它本身不算段的内容——
/// 除了 HEADER 段,那里的变量就挂在它身上。
fn group_sections(
  records : Array[Record],
  warnings : Array[String],
) -> Array[Section] {
  let sections : Array[Section] = []
  let mut current : String? = None
  let mut current_records : Array[Record] = []

  for record in records {
    if record.kind == "SECTION" {
      // 上一个段没关就又开了新的——尽力而为:把上一个收掉
      match current {
        Some(name) => {
          warnings.push(
            "段「\{name}」没有 ENDSEC 就结束了(后面又开了新段)",
          )
          sections.push({ name, records: current_records, })
        }
        None => ()
      }
      current = Some(record.get_string(2))
      current_records = []
      // HEADER 段的变量挂在 SECTION 这条记录上,得留着
      if current == Some("HEADER") {
        current_records.push(record)
      }
    } else if record.kind == "ENDSEC" {
      match current {
        Some(name) => {
          sections.push({ name, records: current_records, })
          current = None
          current_records = []
        }
        None =>
          warnings.push("出现了没有对应 SECTION 的 ENDSEC,已忽略")
      }
    } else {
      current_records.push(record)
    }
  }
  match current {
    Some(name) => {
      warnings.push(
        "文件在段「\{name}」中间就结束了,没有 ENDSEC",
      )
      sections.push({ name, records: current_records, })
    }
    None => ()
  }
  sections
}

///|
/// 从 HEADER 段读一个变量。
///
/// HEADER 变量没有 `0` 分隔,是成对挂在 SECTION 记录里的:
/// `9 / $ACADVER` 后面紧跟一个 tag 就是它的值。
pub fn find_header_var(sections : Array[Section], name : String) -> String? {
  for s in sections {
    if s.name != "HEADER" {
      continue
    }
    for record in s.records {
      let tags = record.common
      let mut i = 0
      while i + 1 < tags.length() {
        if tags[i].code == 9 && tags[i].value.as_string() == name {
          return Some(tags[i + 1].value.as_string())
        }
        i = i + 1
      }
    }
  }
  None
}

///|
/// 取一个段。
pub fn Drawing::section(self : Drawing, name : String) -> Section? {
  for s in self.sections {
    if s.name == name {
      return Some(s)
    }
  }
  None
}