///|
/// Intrinsic size measurement result
pub(all) struct IntrinsicSize {
  min_width : Double
  max_width : Double
  min_height : Double
  max_height : Double
}

///|
/// Default intrinsic size for leaf nodes without measure function
/// Matches Taffy behavior: empty/unmeasured nodes have zero intrinsic size
pub fn IntrinsicSize::default() -> IntrinsicSize {
  { min_width: 0.0, max_width: 0.0, min_height: 0.0, max_height: 0.0 }
}

///|
/// Measure function type - called to determine intrinsic size of a node
/// Parameters: (available_width, available_height) -> IntrinsicSize
pub(all) struct MeasureFunc {
  func : (Double, Double) -> IntrinsicSize
}

///|
/// 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 : 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 : 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 : 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 : MeasureFunc,
  content : String,
) -> Node {
  {
    id,
    uid: next_uid(),
    style,
    children: [],
    measure: Some(measure),
    text: Some(content),
  }
}

///|
/// Complete layout result for a node
pub(all) struct Layout {
  id : String
  x : Double
  y : Double
  width : Double
  height : Double
  // Box model resolved values
  margin : @types.Rect[Double]
  padding : @types.Rect[Double]
  border : @types.Rect[Double]
  // Overflow clipping info
  overflow_x : @style.Overflow
  overflow_y : @style.Overflow
  // Children layouts
  children : Array[Layout]
  // Text content for text nodes
  text : String?
}

///|
pub fn Layout::to_bounding_rect(self : Layout) -> @types.BoundingRect {
  { x: self.x, y: self.y, width: self.width, height: self.height }
}

///|
/// Sizing mode for layout computation
/// Definite: Use available space (normal flow)
/// MaxContent: Use intrinsic max-content (shrink-to-fit)
pub(all) enum SizingMode {
  Definite
  MaxContent
} derive(Eq)

///|
/// Context for layout computation
pub(all) struct LayoutContext {
  /// Available width from parent
  available_width : Double
  /// Available height from parent (may be undefined)
  available_height : Double?
  /// Sizing mode (defaults to MaxContent for root-level intrinsic sizing)
  sizing_mode : SizingMode
  /// Viewport width for position: fixed elements
  viewport_width : Double
  /// Viewport height for position: fixed elements
  viewport_height : Double
}

///|
/// Warnings emitted during layout computation
pub(all) enum LayoutWarning {
  UnsupportedFloat(String) // node id with float: left/right
  UnsupportedPosition(String) // node id with position: absolute/fixed
  UnsupportedDisplay(String) // node id with unsupported display type
} derive(Show)

///|
/// Result of layout computation with warnings
pub(all) struct LayoutResult {
  layout : Layout
  warnings : Array[LayoutWarning]
}

///|
/// Layout dispatch function type
/// Used to delegate layout computation to the appropriate algorithm based on display type
pub(all) struct LayoutDispatchFunc((Node, LayoutContext) -> Layout)

///|
/// Global layout dispatch function
/// Set this before layout computation to enable cross-layout-type dispatch
let layout_dispatcher : Ref[LayoutDispatchFunc?] = { val: None }

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

///|
/// Get the global layout dispatcher (for checking if it's set)
pub fn get_layout_dispatcher() -> LayoutDispatchFunc? {
  layout_dispatcher.val
}

///|
/// Compute layout for a node using the global dispatcher
/// Falls back to returning a minimal layout if no dispatcher is set
pub fn dispatch_layout(node : Node, ctx : LayoutContext) -> Layout {
  match layout_dispatcher.val {
    Some(LayoutDispatchFunc(f)) => f(node, ctx)
    None => {
      // Fallback: create minimal layout from node dimensions
      let width = match node.style.width {
        @types.Length(w) => w
        @types.Percent(p) => ctx.available_width * p
        @types.Auto => ctx.available_width
        @types.MinContent => 0.0 // Fallback: use minimal size
        @types.MaxContent => ctx.available_width
        @types.FitContent(_) => ctx.available_width
      }
      let height = match node.style.height {
        @types.Length(h) => h
        @types.Percent(p) => ctx.available_height.unwrap_or(0.0) * p
        @types.Auto => 0.0
        @types.MinContent => 0.0
        @types.MaxContent => 0.0
        @types.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: @style.Visible,
        overflow_y: @style.Visible,
        children: [],
        text: node.text,
      }
    }
  }
}