///|
/// 块级元素类型。
pub enum BlockKind {
  Heading
  ListItem
  BlockQuote
  TableRow
  TableSeparator
  FencedCode
  IndentedCode
  ThematicBreak
  Blank
  Paragraph
} derive(Eq, Debug)

///|
pub extend BlockKind with Eq::{not_equal, equal}

///|
pub extend BlockKind with @moonbitlang/core/debug.Debug::{to_repr}

///|
/// 列表标记。
pub enum ListMarker {
  /// 无序标记:`-` `*` `+`
  Bullet(Char)
  /// 有序标记:`.` 或 `)`
  Ordered(Char)
} derive(Eq, Debug)

///|
pub extend ListMarker with Eq::{not_equal, equal}

///|
pub extend ListMarker with @moonbitlang/core/debug.Debug::{to_repr}

///|
/// 一个块级元素(以行为单位)。
pub struct Block {
  kind : BlockKind
  /// 该行首字符在全文中的字符偏移
  start : Int
  /// 该行尾字符在全文中的字符偏移(开端,不含换行)
  end : Int
  /// 1 起始行号
  line : Int
  /// 缩进宽度(tab 记为 4)
  indent : Int
  /// 标题层级(1-6);非标题为 0
  level : Int
  /// 列表标记;非列表为 `None`
  marker : ListMarker?
  /// 表格列数;非表格行为 0
  columns : Int
} derive(Eq, Debug)

///|
pub extend Block with Eq::{not_equal, equal}

///|
pub extend Block with @moonbitlang/core/debug.Debug::{to_repr}

///|
/// 计算行首缩进宽度,`tab` 记为 4 列。
pub fn indent_width(chars : Array[Char]) -> Int {
  let mut width = 0
  let mut i = 0
  while i < chars.length() {
    match chars[i] {
      ' ' => width = width + 1
      '\t' => width = width + 4
      _ => break
    }
    i = i + 1
  }
  width
}

///|
/// 是否为空白行(只含空格与制表符)。
pub fn is_blank_line(chars : Array[Char]) -> Bool {
  let mut i = 0
  while i < chars.length() {
    if !is_space_char(chars[i]) {
      return false
    }
    i = i + 1
  }
  true
}

///|
/// 是否为缩进代码块行(缩进 >= 4 且行内有内容)。
///
/// Markdown 中缩进 4 空格(或 1 个制表符)的连续行构成代码块,
/// 其中的内容不应参与排版检查。
pub fn is_indented_code_line(chars : Array[Char]) -> Bool {
  if is_blank_line(chars) {
    return false
  }
  indent_width(chars) >= 4
}

///|
/// 判断一行是否以指定字符开头(忽略行首缩进)。
fn starts_with_char(chars : Array[Char], c : Char) -> Bool {
  let i = first_non_space(chars)
  i < chars.length() && chars[i] == c
}

///|
/// 标题层级:`#` 开头返回 1-6,否则 0。
pub fn heading_level_of(chars : Array[Char]) -> Int {
  let mut i = first_non_space(chars)
  if i >= chars.length() || chars[i] != '#' {
    return 0
  }
  let mut level = 0
  while i < chars.length() && chars[i] == '#' && level < 7 {
    level = level + 1
    i = i + 1
  }
  if level > 6 {
    return 0
  }
  if i < chars.length() && !is_space_char(chars[i]) {
    return 0
  }
  level
}

///|
/// 列表标记:返回标记种类,非列表行为 `None`。
pub fn list_marker_of(chars : Array[Char]) -> ListMarker? {
  let i = first_non_space(chars)
  if i >= chars.length() {
    return None
  }
  let c = chars[i]
  if c == '-' || c == '*' || c == '+' {
    if i + 1 < chars.length() && is_space_char(chars[i + 1]) {
      return Some(Bullet(c))
    }
    return None
  }
  let mut k = i
  while k < chars.length() && chars[k].is_ascii_digit() {
    k = k + 1
  }
  if k > i && k < chars.length() && (chars[k] == '.' || chars[k] == ')') {
    if k + 1 < chars.length() && is_space_char(chars[k + 1]) {
      return Some(Ordered(chars[k]))
    }
  }
  None
}

///|
/// 是否为分割线(`---` / `***` / `___`,至少 3 个)。
pub fn is_thematic_break(chars : Array[Char]) -> Bool {
  let i = first_non_space(chars)
  if i >= chars.length() {
    return false
  }
  let c = chars[i]
  if c != '-' && c != '*' && c != '_' {
    return false
  }
  let mut count = 0
  let mut k = i
  while k < chars.length() {
    let ch = chars[k]
    if ch == c {
      count = count + 1
    } else if !is_space_char(ch) {
      return false
    }
    k = k + 1
  }
  count >= 3
}

///|
/// 统计表格列数;非表格行返回 0。
///
/// 以不在受保护区域内的 `|` 作为分隔符统计,首尾的 `|` 不计入列数。
pub fn table_column_count(chars : Array[Char]) -> Int {
  let start = first_non_space(chars)
  let mut end = chars.length()
  while end > start && is_space_char(chars[end - 1]) {
    end = end - 1
  }
  if end <= start {
    return 0
  }
  let mut pipes = 0
  let mut i = start
  while i < end {
    if chars[i] == '|' {
      pipes = pipes + 1
    }
    i = i + 1
  }
  if pipes == 0 {
    return 0
  }
  if chars[start] == '|' && chars[end - 1] == '|' {
    pipes - 1
  } else if chars[start] == '|' || chars[end - 1] == '|' {
    pipes
  } else {
    pipes + 1
  }
}

///|
/// 是否为 Markdown 表格的分隔行(`|---|---|`、`| :--- | ---: |` 等)。
pub fn is_table_separator(chars : Array[Char]) -> Bool {
  if table_column_count(chars) == 0 {
    return false
  }
  let mut has_dash = false
  let mut i = first_non_space(chars)
  while i < chars.length() {
    match chars[i] {
      '-' => has_dash = true
      '|' | ':' | ' ' | '\t' => ()
      _ => return false
    }
    i = i + 1
  }
  has_dash
}

///|
/// 统计一行中表格单元格的文本内容(去掉首尾空白);非表格行返回空数组。
pub fn table_cells(chars : Array[Char]) -> Array[String] {
  let out : Array[String] = []
  if table_column_count(chars) == 0 {
    return out
  }
  let buf = StringBuilder()
  let mut i = first_non_space(chars)
  let n = chars.length()
  while i < n {
    let c = chars[i]
    if c == '|' {
      out.push(trim_string(buf.to_string()))
      buf.reset()
    } else {
      buf.write_char(c)
    }
    i = i + 1
  }
  let tail = trim_string(buf.to_string())
  if tail != "" {
    out.push(tail)
  }
  out
}

///|
/// 去掉字符串首尾空白。
fn trim_string(s : String) -> String {
  let chars = s.iter().to_array()
  let mut start = 0
  let mut end = chars.length()
  while start < end && is_space_char(chars[start]) {
    start = start + 1
  }
  while end > start && is_space_char(chars[end - 1]) {
    end = end - 1
  }
  chars_to_string(chars, start, end)
}

///|
/// 对单行做块级分类。
fn classify_line(line : SLine) -> BlockKind {
  if line.code {
    if is_indented_code_line(line.chars) {
      IndentedCode
    } else {
      FencedCode
    }
  } else if is_blank_line(line.chars) {
    Blank
  } else if heading_level_of(line.chars) > 0 {
    Heading
  } else if is_thematic_break(line.chars) {
    ThematicBreak
  } else if is_table_separator(line.chars) {
    TableSeparator
  } else if table_column_count(line.chars) > 0 {
    TableRow
  } else if list_marker_of(line.chars) is Some(_) {
    ListItem
  } else if starts_with_char(line.chars, '>') {
    BlockQuote
  } else {
    Paragraph
  }
}

///|
/// 为整个文档构建块序列(每行一个块)。
fn build_blocks(ctx : Ctx) -> Array[Block] {
  let out : Array[Block] = []
  let mut no = 0
  for line in ctx.lines {
    no = no + 1
    let kind = classify_line(line)
    let marker = list_marker_of(line.chars)
    let level = heading_level_of(line.chars)
    let columns = if kind == TableRow || kind == TableSeparator {
      table_column_count(line.chars)
    } else {
      0
    }
    out.push({
      kind,
      start: line.start,
      end: line.start + line.chars.length(),
      line: no,
      indent: indent_width(line.chars),
      level,
      marker,
      columns,
    })
  }
  out
}

///|
/// 分析文本的块级结构(每行一个块)。
///
/// 库级入口,供外部工具直接获取块结构;内部规则复用同一实现。
pub fn analyze_blocks(source : String, markdown : Bool) -> Array[Block] {
  let scan = scan_source(source, markdown)
  let ctx = Ctx::{
    lines: scan.lines,
    config: Config::new(),
    unclosed_fence: scan.unclosed_fence,
  }
  build_blocks(ctx)
}

///|
/// 块级元素类型的显示名。
pub fn BlockKind::label(self : BlockKind) -> String {
  match self {
    Heading => "标题"
    ListItem => "列表项"
    BlockQuote => "引用块"
    TableRow => "表格行"
    TableSeparator => "表格分隔行"
    FencedCode => "围栏代码块"
    IndentedCode => "缩进代码块"
    ThematicBreak => "分割线"
    Blank => "空行"
    Paragraph => "段落"
  }
}