/// 场景图节点模型(docs/02)。
/// children 只存 id,节点本体统一放在 Document.nodes 的平铺表里——
/// 这让补丁、日志、JSON 导出都以 id 为键,人类与 Agent 共享同一份表示。

///|
/// 节点种类
pub(all) enum NodeKind {
  Frame // 容器:可携带栈布局,子节点按布局或绝对定位排布
  Group // 透明容器:仅分组,子节点一律绝对定位
  Rect // 矩形
  Text // 文本
  Image // 图片(v1 渲染为占位矩形)
}

///|
/// 尺寸规格:Fixed 定值 / Fill 占满可用空间 / Hug 包裹内容
pub(all) enum SizeSpec {
  Fixed(Double)
  Fill
  Hug
}

///|
/// 位置规格(相对父容器内容盒;栈布局中主轴分量被忽略,由布局游标决定)
pub(all) enum PosSpec {
  Fixed(Double)
  AlignStart(Double) // 距内容盒 start 边的偏移
  AlignEnd(Double) // 距内容盒 end 边的偏移
  CenterInParent
}

///|
pub(all) enum StackDir {
  Vertical
  Horizontal
}

///|
/// 主轴对齐:剩余空间(无 Fill 子项时)如何分配
pub(all) enum Justify {
  Start
  Center
  End
}

///|
pub(all) struct Padding {
  top : Double
  right : Double
  bottom : Double
  left : Double
}

///|
pub fn Padding::uniform(v : Double) -> Padding {
  { top: v, right: v, bottom: v, left: v, }
}

///|
/// 栈布局:Figma Auto Layout 的最小可用子集
pub(all) struct StackLayout {
  dir : StackDir
  gap : Double
  justify : Justify
  padding : Padding
}

///|
/// 视觉样式(v4:+ 文本对齐 / 斜体 / 描边虚线 / 可见性)
pub(all) struct NodeStyle {
  fill : String
  stroke : String
  stroke_width : Double
  corner_radius : Double
  opacity : Double
  font_size : Double
  text_color : String
  font_weight : String
  shadow : String // "none" | "e1" | "e2" | "e3"
  rotate : Double // 旋转角度(度,绕节点中心)
  blur : Double // 层模糊半径 px(0 = 无)
  blend : String // 混合模式 "normal"|"multiply"|"screen"|"overlay"
  line_height : Double // 文本行高倍数(默认 1.5)
  tracking : Double // 字距(em)
  flip : String // "none" | "h" | "v" | "both"
  text_align : String // 文本水平对齐 "left" | "center" | "right"
  font_style : String // "normal" | "italic"
  stroke_dash : String // 描边线型 "solid" | "dashed" | "dotted"
  visible : Bool // false = 隐藏但仍存在于文档(不参与渲染与命中)
}

///|
pub fn NodeStyle::default() -> NodeStyle {
  {
    fill: "none",
    stroke: "none",
    stroke_width: 0.0,
    corner_radius: 0.0,
    opacity: 1.0,
    font_size: 14.0,
    text_color: "#111111",
    font_weight: "normal",
    shadow: "none",
    rotate: 0.0,
    blur: 0.0,
    blend: "normal",
    line_height: 1.5,
    tracking: 0.0,
    flip: "none",
    text_align: "left",
    font_style: "normal",
    stroke_dash: "solid",
    visible: true,
  }
}

///|
pub(all) struct Node {
  id : String
  kind : NodeKind
  name : String
  /// 语义组件名("button" / "text_input" / "frame"...):
  /// decl 声明文本序列化/解析据此保留组件语义,编辑器组件库也用它
  component : String
  width : SizeSpec
  height : SizeSpec
  x : PosSpec
  y : PosSpec
  layout : StackLayout? // 仅 Frame 使用;None = 绝对定位容器
  style : NodeStyle
  text : String // 仅 Text 使用;Rect 上非空为居中标签
  children : Array[String]
  constraint : String // 父容器 resize 时的适配约束(Figma Constraints):
  // 水平 lt|ct|rt|stretch|hscale + 垂直组合,如 "lt" "rt" "ct" "cb" "stretch-c" "hscale-m"
}

///|
fn kind_component(kind : NodeKind) -> String {
  match kind {
    Frame => "frame"
    Group => "group"
    Rect => "rect"
    Text => "text"
    Image => "image"
  }
}

///|
/// 带默认值的节点工厂,配合结构更新语法使用:{ ..Node::make(id, kind), ... }
pub fn Node::make(id : String, kind : NodeKind) -> Node {
  {
    id,
    kind,
    name: id,
    component: kind_component(kind),
    width: Fixed(100.0),
    height: Fixed(100.0),
    x: Fixed(0.0),
    y: Fixed(0.0),
    layout: None,
    style: NodeStyle::default(),
    text: "",
    children: [],
    constraint: "lt",
  }
}

///|
/// 文本内在尺寸估算(v1:CJK 全角按 1.0 倍字号,其余 0.6 倍;行高 1.5)。
/// M2 计划:编辑器运行时用 canvas measureText 实测后写入文档,见 docs/03。
pub const TEXT_LINE_HEIGHT_RATIO : Double = 1.5

///|
pub const CJK_CODEPOINT_MIN : Int = 0x2E80

///|
pub const ASCII_ADVANCE_RATIO : Double = 0.6

///|
fn char_advance(code : Int, font_size : Double) -> Double {
  if code >= CJK_CODEPOINT_MIN {
    font_size
  } else {
    font_size * ASCII_ADVANCE_RATIO
  }
}

///|
pub fn text_intrinsic(
  text : String,
  font_size : Double,
  line_height? : Double = 1.5,
) -> Size {
  // 多行:取最宽行,高度 = 行数 × 行高
  let mut max_w = 0.0
  let mut lines = 0
  let mut line_w = 0.0
  for c in text.iter() {
    if c == '\n' {
      if line_w > max_w {
        max_w = line_w
      }
      line_w = 0.0
      lines = lines + 1
    } else {
      line_w = line_w + char_advance(c.to_int(), font_size)
    }
  }
  if line_w > max_w {
    max_w = line_w
  }
  lines = lines + 1
  let w = if max_w == 0.0 { font_size * ASCII_ADVANCE_RATIO } else { max_w }
  { width: w, height: font_size * line_height * lines.to_double(), }
}