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

///|
/// Generate a new unique node ID
fn next_uid() -> Int {
  let uid = node_uid_counter.val
  node_uid_counter.val = uid + 1
  uid
}

///|
/// A node in the layout tree
pub struct Node {
  id : String
  uid : Int // Unique identifier for caching
  style : @style.Style
  children : Array[Node]
  measure : @types.MeasureFunc?
  text : String? // Text content for text nodes
}

///|
pub fn Node::new(
  id : String,
  style : @style.Style,
  children : Array[Node],
) -> Node {
  { id, uid: next_uid(), style, children, measure: None, text: None }
}

///|
/// Create a node with a specific uid (for incremental layout)
pub fn Node::with_uid(
  id : String,
  uid : Int,
  style : @style.Style,
  children : Array[Node],
) -> Node {
  { id, uid, style, children, measure: None, text: None }
}

///|
/// Create a node with a specific uid and measure function
pub fn Node::with_uid_and_measure(
  id : String,
  uid : Int,
  style : @style.Style,
  children : Array[Node],
  measure : @types.MeasureFunc?,
  text : String?,
) -> Node {
  { id, uid, style, children, measure, text }
}

///|
pub fn Node::leaf(id : String, style : @style.Style) -> Node {
  { id, uid: next_uid(), style, children: [], measure: None, text: None }
}

///|
/// Create a leaf node with a custom measure function
/// Optional text parameter for alt text (images) or other content
pub fn Node::with_measure(
  id : String,
  style : @style.Style,
  measure : @types.MeasureFunc,
  text? : String,
) -> Node {
  { id, uid: next_uid(), style, children: [], measure: Some(measure), text }
}

///|
/// Create a text node with content
pub fn Node::text(
  id : String,
  style : @style.Style,
  measure : @types.MeasureFunc,
  content : String,
) -> Node {
  {
    id,
    uid: next_uid(),
    style,
    children: [],
    measure: Some(measure),
    text: Some(content),
  }
}

///|
/// Dispatch function type - passed through layout computation to enable
/// cross-layout-type dispatch without global state.
/// The function takes (node, context, dispatch) and returns Layout.
pub(all) struct DispatchFn(
  (Node, @types.LayoutContext, DispatchFn) -> @types.Layout
)

///|
/// @deprecated Use DispatchFn instead
/// Layout dispatch function type (kept for backward compatibility with incremental compute)
pub(all) struct LayoutDispatchFunc(
  (Node, @types.LayoutContext) -> @types.Layout
)

///|
/// Global layout dispatch function (for incremental compute caching)
let layout_dispatcher : Ref[LayoutDispatchFunc?] = { val: None }

///|
/// Set the global layout dispatcher (used by incremental compute for caching)
pub fn set_layout_dispatcher(f : LayoutDispatchFunc) -> Unit {
  layout_dispatcher.val = Some(f)
}

///|
/// Get the global layout dispatcher
pub fn get_layout_dispatcher() -> LayoutDispatchFunc? {
  layout_dispatcher.val
}

///|
/// Create a minimal fallback layout for a node (used when dispatch is unavailable)
pub fn fallback_layout(
  node : Node,
  ctx : @types.LayoutContext,
) -> @types.Layout {
  let width = match node.style.width {
    @types.Dimension::Length(w) => w
    @types.Dimension::Percent(p) => ctx.available_width * p
    @types.Dimension::Auto => ctx.available_width
    @types.Dimension::MinContent => 0.0
    @types.Dimension::MaxContent => ctx.available_width
    @types.Dimension::FitContent(_) => ctx.available_width
  }
  let height = match node.style.height {
    @types.Dimension::Length(h) => h
    @types.Dimension::Percent(p) => ctx.available_height.unwrap_or(0.0) * p
    @types.Dimension::Auto => 0.0
    @types.Dimension::MinContent => 0.0
    @types.Dimension::MaxContent => 0.0
    @types.Dimension::FitContent(_) => 0.0
  }
  {
    id: node.id,
    x: 0.0,
    y: 0.0,
    width,
    height,
    margin: @types.Rect::zero(),
    padding: @types.Rect::zero(),
    border: @types.Rect::zero(),
    overflow_x: @types.Visible,
    overflow_y: @types.Visible,
    scroll_width: width,
    scroll_height: height,
    children: [],
    text: node.text,
  }
}