// =============================================================================
// Layout Package - Public API Entry Point
// =============================================================================
// This package re-exports Node and LayoutTree related types.
// For enum types (Display, Dimension, etc.), import
// "mizchi/crater-layout/types" directly.
//
// Usage:
//   import {
//     "mizchi/crater-layout/types",
//     "mizchi/crater-layout",
//   }
//   let style = @layout.Style::default()
//   style.display = @types.Flex
//   let node = @layout.Node::new("root", style, [])

// =============================================================================
// Re-exports from style
// =============================================================================

///|
pub using @style {type Style}

// =============================================================================
// Re-exports from node
// =============================================================================

///|
pub using @node {type Node}

///|
pub using @node {type DispatchFn, type LayoutDispatchFunc}

// =============================================================================
// Re-exports from tree
// =============================================================================

///|
pub using @tree {type LayoutNode, type LayoutTree, type CacheStats}

///|
pub using @tree {type IntrinsicState, type ResourceId}

// =============================================================================
// Convenience Constructors
// =============================================================================

///|
/// Create a new Size for viewport dimensions
pub fn size(width : Double, height : Double) -> @types.Size[Double] {
  @types.Size::new(width, height)
}

///|
/// Create a LayoutNode with default style (builder pattern start)
pub fn layout_node_create(id : String) -> @tree.LayoutNode {
  @tree.LayoutNode::create_with_id(id)
}

///|
/// Create a new LayoutTree from a LayoutNode
pub fn layout_tree(
  root : @tree.LayoutNode,
  viewport_width : Double,
  viewport_height : Double,
) -> @tree.LayoutTree {
  @tree.LayoutTree::new(root, viewport_width, viewport_height)
}

///|
/// Create a LayoutTree from an immutable Node
pub fn layout_tree_from_node(
  root : @node.Node,
  viewport_width : Double,
  viewport_height : Double,
) -> @tree.LayoutTree {
  @tree.LayoutTree::from_node(root, viewport_width, viewport_height)
}

///|
fn default_dispatch() -> @node.DispatchFn {
  @node.DispatchFn(fn(node, ctx, dispatch) {
    @dispatch.compute(node, ctx, dispatch)
  })
}

///|
/// Initialize the global layout dispatcher used by incremental layout.
pub fn setup() -> Unit {
  @dispatch.setup()
}

///|
/// Reset all intrinsic size caches.
pub fn reset_intrinsic_cache() -> Unit {
  @dispatch.reset_intrinsic_cache()
}

///|
/// Clear intrinsic cache for a specific node uid.
pub fn clear_intrinsic_cache_for_node(uid : Int) -> Unit {
  @dispatch.clear_intrinsic_cache_for_node(uid)
}

///|
/// Compute layout for a node tree with given viewport.
pub fn compute_layout(
  node : @node.Node,
  viewport : @types.Size[Double],
) -> @layout_types.Layout {
  let ctx : @layout_types.LayoutContext = {
    available_width: viewport.width,
    available_height: Some(viewport.height),
    sizing_mode: @layout_types.MaxContent,
    viewport_width: viewport.width,
    viewport_height: viewport.height,
    stretch_width: false,
    stretch_height: false,
  }
  dispatch_compute(node, ctx)
}

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

///|
fn dispatch_compute(
  node : @node.Node,
  ctx : @layout_types.LayoutContext,
) -> @layout_types.Layout {
  let dispatch = default_dispatch()
  match node.style.display {
    @types.Flex | @types.InlineFlex => @flex.compute(node, ctx, dispatch)
    @types.Block | @types.Inline | @types.InlineBlock | @types.FlowRoot =>
      @block.compute(node, ctx, dispatch)
    _ => @dispatch.compute_layout(node, ctx)
  }
}

///|
fn dispatch_compute_with_warnings(
  node : @node.Node,
  ctx : @layout_types.LayoutContext,
) -> @layout_types.LayoutResult {
  let dispatch = default_dispatch()
  match node.style.display {
    @types.Flex | @types.InlineFlex =>
      @flex.compute_with_warnings(node, ctx, dispatch)
    @types.Block | @types.Inline | @types.InlineBlock | @types.FlowRoot =>
      @block.compute_with_warnings(node, ctx, dispatch)
    _ => {
      let layout = @dispatch.compute_layout(node, ctx)
      { layout, warnings: [] }
    }
  }
}

///|
/// Compute layout with an explicit LayoutContext.
pub fn compute_layout_in_context(
  node : @node.Node,
  ctx : @layout_types.LayoutContext,
) -> @layout_types.Layout {
  // Reset per-render intrinsic-width memoization once at the top-level entry
  // (this function delegates to @dispatch.compute_layout and is not re-entered
  // recursively, so a single clear bounds the caches to one render).
  @block.clear_block_intrinsic_caches()
  @dispatch.compute_layout(node, ctx)
}

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

///|
pub fn compute_block_layout_with_dispatch(
  node : @node.Node,
  ctx : @layout_types.LayoutContext,
  dispatch : @node.DispatchFn,
) -> @layout_types.Layout {
  @block.compute(node, ctx, dispatch)
}

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

///|
pub fn compute_flex_layout_with_dispatch(
  node : @node.Node,
  ctx : @layout_types.LayoutContext,
  dispatch : @node.DispatchFn,
) -> @layout_types.Layout {
  @flex.compute(node, ctx, dispatch)
}

///|
pub fn compute_grid_layout_in_context(
  node : @node.Node,
  ctx : @layout_types.LayoutContext,
  dispatch : @node.DispatchFn,
) -> @layout_types.Layout {
  @grid.compute(node, ctx, dispatch)
}

///|
pub fn compute_grid_layout(
  node : @node.Node,
  available_width : Double,
  available_height : Double,
) -> @layout_types.Layout {
  @grid.compute_layout(node, available_width, available_height)
}