///|
/// Layout-side metadata for a single child of a custom children layout.
///
/// Replaces the previous parallel arrays (`child_sizes`, `child_baselines`,
/// `child_priorities`, `child_alignment_guides`) with a single, typed record
/// per child. The runtime fills this from the measured/placed child element,
/// so custom layouts no longer have to rely on guessed baseline values or
/// hard-coded priorities.
pub(all) struct ChildLayoutInfo {
  /// Measured size of the child after its own layout pass.
  size : Size
  /// First baseline of the child, relative to its top edge. `None` means the
  /// child does not expose a baseline; layout authors should fall back to a
  /// reasonable default (e.g. `size.height * 0.8`) rather than treating the
  /// child as baseline-less.
  first_baseline : Double?
  /// Layout priority declared by the child (higher wins space disputes).
  /// Children that do not declare a priority report `0.0`.
  priority : Double
  /// Alignment guides derived from the child size and baseline. Computed by
  /// the runtime so authors do not have to reconstruct them per child.
  alignment_guides : AlignmentGuideSet
  /// Flex weight declared by the child. `0.0` means the child is not flex.
  flex_weight : Double
} derive(Eq, Debug)

///|
/// Construct a `ChildLayoutInfo` from measured child data. The runtime uses
/// this when assembling `CustomLayoutContext.children`; downstream packages
/// use it when building tests for custom layout delegates.
pub fn ChildLayoutInfo::new(
  size~ : Size,
  first_baseline? : Double? = None,
  priority? : Double = 0.0,
  flex_weight? : Double = 0.0,
) -> ChildLayoutInfo {
  {
    size,
    first_baseline,
    priority,
    flex_weight,
    alignment_guides: AlignmentGuideSet::from_size(size, first_baseline~),
  }
}

///|
/// Convenience accessor: the child's resolved baseline, falling back to the
/// historical `size.height * 0.8` estimate when the child does not expose one.
/// Keeping the fallback here lets custom layouts stop special-casing the
/// "no baseline" branch while still preserving prior behavior for children
/// that genuinely lack a baseline.
pub fn ChildLayoutInfo::baseline_or_estimate(self : ChildLayoutInfo) -> Double {
  self.first_baseline.unwrap_or(self.size.height * 0.8)
}

///|
pub(all) struct AlignmentGuideSet {
  leading : Double
  center : Double
  trailing : Double
  first_baseline : Double?
} derive(Eq, Debug, ToJson)

///|
/// Layout context for a custom children-layout delegate. The runtime measures
/// each child first, then hands the resulting `children` array (plus the
/// ambient environment, viewport and reading direction) to the delegate's
/// `measure` callback.
///
/// The previous API exposed `cache` / `cache_key` and several parallel arrays
/// (`child_sizes`, `child_baselines`, …). The cache was mutable state stored
/// on the immutable declaration node (a violation of `view_node.mbt`'s contract)
/// and the parallel arrays made it easy to read the wrong index. Both are gone
/// in favor of `children : Array[ChildLayoutInfo]`.
pub(all) struct CustomLayoutContext {
  constraints : Constraints
  children : Array[ChildLayoutInfo]
  layout_direction : LayoutDirection
  safe_area : Insets
  viewport : Size?
}

///|
/// Placement context handed to a custom children-layout delegate's `place`
/// callback. `children` mirrors the order of `CustomLayoutContext.children`;
/// the delegate returns one `Rect` per child in that same order.
pub(all) struct CustomPlacementContext {
  frame : Rect
  measured_size : Size
  children : Array[ChildLayoutInfo]
  layout_direction : LayoutDirection
  safe_area : Insets
  viewport : Size?
}

///|
pub fn AlignmentGuideSet::from_size(
  size : Size,
  first_baseline? : Double? = None,
) -> AlignmentGuideSet {
  {
    leading: 0.0,
    center: size.width / 2.0,
    trailing: size.width,
    first_baseline,
  }
}

///|
/// Mirror an LTR x coordinate into the RTL frame. Layout authors should pass
/// every child x through this helper so a single placement routine works in
/// both reading directions.
pub fn CustomPlacementContext::mirror_x(
  self : CustomPlacementContext,
  x~ : Double,
  width~ : Double,
) -> Double {
  match self.layout_direction {
    LeftToRight => x
    RightToLeft =>
      self.frame.origin.x +
      self.frame.size.width -
      (x - self.frame.origin.x) -
      width
  }
}