///|
/// LayoutNode - Mutable node for incremental layout computation
/// Unlike Node (immutable), LayoutNode tracks dirty state and caches results

///|
/// Global counter for generating unique layout node IDs
let layout_node_uid_counter : Ref[Int] = { val: 0 }

///|
fn next_layout_uid() -> Int {
  let uid = layout_node_uid_counter.val
  layout_node_uid_counter.val = uid + 1
  uid
}

///|
/// Constraint key for cache lookup
pub struct ConstraintKey {
  available_width : Double
  available_height : Double? // None means unconstrained
  sizing_mode : @layout_types.SizingMode
} derive(Eq)

///|
pub fn ConstraintKey::new(
  available_width : Double,
  available_height : Double?,
  sizing_mode : @layout_types.SizingMode,
) -> ConstraintKey {
  { available_width, available_height, sizing_mode }
}

///|
/// Check if two constraints are approximately equal
pub fn ConstraintKey::approx_eq(
  self : ConstraintKey,
  other : ConstraintKey,
) -> Bool {
  let width_eq = (self.available_width - other.available_width).abs() < 0.001
  let height_eq = match (self.available_height, other.available_height) {
    (Some(a), Some(b)) => (a - b).abs() < 0.001
    (None, None) => true
    _ => false
  }
  width_eq && height_eq && self.sizing_mode == other.sizing_mode
}

///|
/// Cached layout result
pub struct LayoutCache {
  constraint : ConstraintKey
  result : @layout_types.Layout
}

///|
/// Mutable layout node with dirty tracking
pub struct LayoutNode {
  id : String
  uid : Int
  style : @style.Style
  children : Array[LayoutNode]
  mut measure : @layout_types.MeasureFunc?
  text : String?
  // Dirty bits
  mut dirty : Bool
  mut children_dirty : Bool
  // Cache
  mut cached_layout : LayoutCache?
  // Dependencies (computed from style)
  dependencies : DependencyKind
  // Computed layout results (Yoga-compatible)
  mut computed_x : Double
  mut computed_y : Double
  mut computed_width : Double
  mut computed_height : Double
  mut has_new_layout : Bool
  // Intrinsic state for replaceable elements (images, etc.)
  mut intrinsic_state : IntrinsicState?
  // Callback for propagating dirty to parent chain (set by LayoutTree)
  mut on_dirty : ((Int) -> Unit)?
}

///|
pub fn LayoutNode::new(
  id : String,
  style : @style.Style,
  children : Array[LayoutNode],
) -> LayoutNode {
  {
    id,
    uid: next_layout_uid(),
    style,
    children,
    measure: None,
    text: None,
    dirty: true, // Initially dirty
    children_dirty: false,
    cached_layout: None,
    dependencies: analyze_style_dependencies(style),
    computed_x: 0.0,
    computed_y: 0.0,
    computed_width: 0.0,
    computed_height: 0.0,
    has_new_layout: false,
    intrinsic_state: None,
    on_dirty: None,
  }
}

///|
pub fn LayoutNode::leaf(id : String, style : @style.Style) -> LayoutNode {
  {
    id,
    uid: next_layout_uid(),
    style,
    children: [],
    measure: None,
    text: None,
    dirty: true,
    children_dirty: false,
    cached_layout: None,
    dependencies: analyze_style_dependencies(style),
    computed_x: 0.0,
    computed_y: 0.0,
    computed_width: 0.0,
    computed_height: 0.0,
    has_new_layout: false,
    intrinsic_state: None,
    on_dirty: None,
  }
}

///|
pub fn LayoutNode::with_measure(
  id : String,
  style : @style.Style,
  measure : @layout_types.MeasureFunc,
) -> LayoutNode {
  {
    id,
    uid: next_layout_uid(),
    style,
    children: [],
    measure: Some(measure),
    text: None,
    dirty: true,
    children_dirty: false,
    cached_layout: None,
    dependencies: analyze_style_dependencies(style),
    computed_x: 0.0,
    computed_y: 0.0,
    computed_width: 0.0,
    computed_height: 0.0,
    has_new_layout: false,
    intrinsic_state: None,
    on_dirty: None,
  }
}

///|
/// Mark this node as dirty and invalidate cache
/// Also propagates children_dirty to parent chain via callback
pub fn LayoutNode::mark_dirty(self : LayoutNode) -> Unit {
  if self.dirty {
    return // Already dirty
  }
  self.dirty = true
  self.cached_layout = None
  // Also clear intrinsic size cache for this node
  @dispatch.clear_intrinsic_cache_for_node(self.uid)
  // Propagate children_dirty to parent chain
  match self.on_dirty {
    Some(callback) => callback(self.uid)
    None => ()
  }
}

///|
/// Mark that children need re-layout
pub fn LayoutNode::mark_children_dirty(self : LayoutNode) -> Unit {
  if self.children_dirty {
    return
  }
  self.children_dirty = true
}

///|
/// Clear dirty bits after layout
pub fn LayoutNode::clear_dirty(self : LayoutNode) -> Unit {
  self.dirty = false
  self.children_dirty = false
}

///|
/// Check if this node or any children need layout
pub fn LayoutNode::needs_layout(self : LayoutNode) -> Bool {
  self.dirty || self.children_dirty
}

///|
/// Convert LayoutNode to immutable Node (for existing layout functions)
/// Preserves uid for cache lookup
pub fn LayoutNode::to_node(self : LayoutNode) -> @node.Node {
  let children = self.children.map(fn(child) { child.to_node() })
  @node.Node::with_uid_and_measure(
    self.id,
    self.uid,
    self.style,
    children,
    self.measure,
    self.text,
  )
}

///|
/// Create LayoutNode from immutable Node
pub fn LayoutNode::from_node(node : @node.Node) -> LayoutNode {
  let children = node.children.map(fn(child) { LayoutNode::from_node(child) })
  {
    id: node.id,
    uid: node.uid,
    style: node.style,
    children,
    measure: node.measure,
    text: node.text,
    dirty: true,
    children_dirty: false,
    cached_layout: None,
    dependencies: analyze_style_dependencies(node.style),
    computed_x: 0.0,
    computed_y: 0.0,
    computed_width: 0.0,
    computed_height: 0.0,
    has_new_layout: false,
    intrinsic_state: None,
    on_dirty: None,
  }
}

///|
/// Configure a node to use a resolved intrinsic size.
pub fn LayoutNode::set_intrinsic_resolved(
  self : LayoutNode,
  width : Double,
  height : Double,
) -> Unit {
  self.intrinsic_state = Some(Resolved(width~, height~))
  self.measure = Some(create_fixed_measure(width, height))
}

///|
/// Configure a node to use a pending intrinsic placeholder size.
pub fn LayoutNode::set_intrinsic_pending(
  self : LayoutNode,
  placeholder_width : Double,
  placeholder_height : Double,
) -> Unit {
  self.intrinsic_state = Some(Pending(placeholder_width~, placeholder_height~))
  self.measure = Some(
    create_fixed_measure(placeholder_width, placeholder_height),
  )
}