// =============================================================================
// Layout Dispatcher
// =============================================================================

///|
/// Compute layout for a node tree with given viewport
/// Dispatches to the appropriate layout algorithm based on display type
pub fn compute_layout(
  node : @node.Node,
  viewport : @types.Size[Double],
) -> @node.Layout {
  let ctx : @node.LayoutContext = {
    available_width: viewport.width,
    available_height: Some(viewport.height),
    sizing_mode: @node.MaxContent,
    viewport_width: viewport.width,
    viewport_height: viewport.height,
  }
  dispatch_compute(node, ctx)
}

///|
/// Compute layout with warnings
pub fn compute_layout_with_warnings(
  node : @node.Node,
  viewport : @types.Size[Double],
) -> @node.LayoutResult {
  let ctx : @node.LayoutContext = {
    available_width: viewport.width,
    available_height: Some(viewport.height),
    sizing_mode: @node.MaxContent,
    viewport_width: viewport.width,
    viewport_height: viewport.height,
  }
  dispatch_compute_with_warnings(node, ctx)
}

///|
/// Dispatch to appropriate layout algorithm based on display type
fn dispatch_compute(
  node : @node.Node,
  ctx : @node.LayoutContext,
) -> @node.Layout {
  match node.style.display {
    @style.Flex | @style.InlineFlex => @flex.compute(node, ctx)
    @style.Block | @style.Inline | @style.InlineBlock | @style.FlowRoot =>
      @block.compute(node, ctx)
    @style.Grid | @style.InlineGrid =>
      @grid.compute_grid_layout(
        node,
        ctx.available_width,
        ctx.available_height.unwrap_or(0.0),
      )
    @style.Table | @style.InlineTable => @table.compute(node, ctx)
    @style.None | @style.Contents =>
      {
        // display: none/contents - return zero-sized layout
        id: node.id,
        x: 0.0,
        y: 0.0,
        width: 0.0,
        height: 0.0,
        margin: @types.Rect::zero(),
        padding: @types.Rect::zero(),
        border: @types.Rect::zero(),
        overflow_x: @style.Visible,
        overflow_y: @style.Visible,
        children: [],
        text: node.text,
      }
    // Table sub-elements - handled as blocks when encountered outside table context
    @style.TableRow
    | @style.TableCell
    | @style.TableCaption
    | @style.TableRowGroup
    | @style.TableHeaderGroup
    | @style.TableFooterGroup
    | @style.TableColumn
    | @style.TableColumnGroup => @block.compute(node, ctx)
  }
}

///|
/// Dispatch with warnings
fn dispatch_compute_with_warnings(
  node : @node.Node,
  ctx : @node.LayoutContext,
) -> @node.LayoutResult {
  match node.style.display {
    @style.Flex | @style.InlineFlex => @flex.compute_with_warnings(node, ctx)
    @style.Block | @style.Inline | @style.InlineBlock | @style.FlowRoot =>
      @block.compute_with_warnings(node, ctx)
    @style.Grid | @style.InlineGrid => {
      let layout = @grid.compute_grid_layout(
        node,
        ctx.available_width,
        ctx.available_height.unwrap_or(0.0),
      )
      { layout, warnings: [] }
    }
    @style.Table | @style.InlineTable => {
      let layout = @table.compute(node, ctx)
      { layout, warnings: [] }
    }
    @style.None | @style.Contents =>
      {
        layout: {
          id: node.id,
          x: 0.0,
          y: 0.0,
          width: 0.0,
          height: 0.0,
          margin: @types.Rect::zero(),
          padding: @types.Rect::zero(),
          border: @types.Rect::zero(),
          overflow_x: @style.Visible,
          overflow_y: @style.Visible,
          children: [],
          text: node.text,
        },
        warnings: [],
      }
    // Table sub-elements - handled as blocks when encountered outside table context
    @style.TableRow
    | @style.TableCell
    | @style.TableCaption
    | @style.TableRowGroup
    | @style.TableHeaderGroup
    | @style.TableFooterGroup
    | @style.TableColumn
    | @style.TableColumnGroup => @block.compute_with_warnings(node, ctx)
  }
}

///|
/// Compute block layout directly
pub fn compute_block_layout(
  node : @node.Node,
  ctx : @node.LayoutContext,
) -> @node.Layout {
  @block.compute(node, ctx)
}

///|
/// Compute flex layout directly
pub fn compute_flex_layout(
  node : @node.Node,
  ctx : @node.LayoutContext,
) -> @node.Layout {
  @flex.compute(node, ctx)
}

///|
/// Compute element shift (CLS)
pub fn compute_element_shift(
  before : @types.BoundingRect,
  after : @types.BoundingRect,
  viewport : @types.Size[Double],
) -> @webvitals.LayoutShift {
  @webvitals.compute_element_shift(before, after, viewport)
}

///|
/// Compute total CLS score
pub fn compute_total_cls(
  before : Array[@types.BoundingRect],
  after : Array[@types.BoundingRect],
  viewport : @types.Size[Double],
) -> Double {
  @webvitals.compute_total(before, after, viewport)
}