// =============================================================================
// Multi-column / Fragmentation Parsing
// =============================================================================

///|
fn parse_column_count(value : String) -> Int? {
  let trimmed = value.trim().to_owned()
  if trimmed.is_empty() || trimmed == "auto" {
    return None
  }
  try {
    let parsed = @string.parse_int(trimmed)
    if parsed >= 1 {
      Some(parsed)
    } else {
      None
    }
  } catch {
    _ => None
  }
}

///|
fn parse_column_width(value : String, ctx : ComputeContext) -> @types.Dimension {
  let trimmed = value.trim().to_owned()
  if trimmed.is_empty() || trimmed == "auto" {
    return Auto
  }
  let dim = resolve_dimension_with_calc_percent_fallback(trimmed, ctx)
  match dim {
    Length(v) => if v > 0.0 { Length(v) } else { Auto }
    Percent(v) => if v > 0.0 { Percent(v) } else { Auto }
    Calc(px, pct) => if px > 0.0 || pct > 0.0 { Calc(px, pct) } else { Auto }
    MathFn(op, args) => MathFn(op, args)
    Auto | MinContent | MaxContent | FitContent(_) => Auto
  }
}

///|
fn parse_column_fill(value : String) -> @style.ColumnFill {
  match value.trim().to_owned() {
    "auto" => Auto
    _ => Balance
  }
}

///|
fn parse_break_before(value : String) -> @style.BreakBefore {
  match value.trim().to_owned() {
    "column" => Column
    "avoid" => Avoid
    _ => Auto
  }
}

///|
fn parse_break_inside(value : String) -> @style.BreakInside {
  match value.trim().to_owned() {
    "avoid" => Avoid
    _ => Auto
  }
}

///|
fn parse_column_span(value : String) -> @style.ColumnSpan {
  match value.trim().to_owned() {
    "all" => All
    _ => None
  }
}

///|
fn parse_columns_shorthand(
  value : String,
  ctx : ComputeContext,
) -> (Int?, @types.Dimension)? {
  let parts : Array[StringView] = value
    .trim()
    .split(" ")
    .filter(fn(s) { !s.is_empty() })
    .collect()
  if parts.length() == 0 {
    return None
  }
  let mut count : Int? = None
  let mut width = @types.Dimension::Auto
  for part in parts {
    let token = part.to_owned()
    if token == "auto" {
      continue
    }
    if count is None {
      match parse_column_count(token) {
        Some(v) => {
          count = Some(v)
          continue
        }
        None => ()
      }
    }
    let parsed_width = parse_column_width(token, ctx)
    match parsed_width {
      Auto => ()
      _ => width = parsed_width
    }
  }
  Some((count, width))
}

// =============================================================================
// Containment Parsing
// =============================================================================

///|
/// Parse contain-intrinsic-inline-size / contain-intrinsic-block-size.
/// Returns a content-box fallback length in px, or None for none/auto.
fn parse_contain_intrinsic_axis(
  value : String,
  ctx : ComputeContext,
) -> Double? {
  let v = resolve_all_vars(value, ctx).trim().to_owned()
  if v.is_empty() || v == "none" || v == "auto" {
    return None
  }
  match resolve_dimension(v, ctx) {
    Length(length) => if length > 0.0 { Some(length) } else { Some(0.0) }
    _ => None
  }
}

///|
/// Parse contain-intrinsic-size shorthand.
/// One value applies to both inline/block axes; two values map to inline then block.
fn parse_contain_intrinsic_size(
  value : String,
  ctx : ComputeContext,
) -> (Double?, Double?)? {
  let v = resolve_all_vars(value, ctx).trim().to_owned()
  if v.is_empty() {
    return None
  }
  let axis_values : Array[Double?] = []
  let parts = v.split(" ")
  for part in parts {
    let p = part.to_owned().trim().to_owned()
    if p.is_empty() || p == "auto" {
      continue
    }
    if p == "none" {
      axis_values.push(None)
      continue
    }
    match parse_contain_intrinsic_axis(p, ctx) {
      Some(length) => axis_values.push(Some(length))
      None => ()
    }
  }
  if axis_values.length() == 0 {
    None
  } else if axis_values.length() == 1 {
    Some((axis_values[0], axis_values[0]))
  } else {
    Some((axis_values[0], axis_values[1]))
  }
}

///|
/// Parse contain property
fn parse_contain(value : String) -> @style.Contain {
  let v = value.trim().to_owned()
  // Handle keywords
  if v == "none" {
    return @style.Contain::none()
  }
  if v == "strict" {
    return @style.Contain::strict()
  }
  if v == "content" {
    return @style.Contain::content()
  }
  // Parse space-separated values
  let mut result = @style.Contain::none()
  let parts = v.split(" ")
  for part in parts {
    let p = part.to_owned().trim()
    if p == "size" {
      result = { ..result, size: true }
    } else if p == "inline-size" {
      result = { ..result, inline_size: true }
    } else if p == "layout" {
      result = { ..result, layout: true }
    } else if p == "paint" {
      result = { ..result, paint: true }
    } else if p == "style" {
      result = { ..result, style: true }
    }
  }
  result
}

///|
fn merge_contain(
  base : @style.Contain,
  extra : @style.Contain,
) -> @style.Contain {
  let merged : @style.Contain = {
    size: base.size || extra.size,
    inline_size: base.inline_size || extra.inline_size,
    layout: base.layout || extra.layout,
    paint: base.paint || extra.paint,
    style: base.style || extra.style,
  }
  if merged.size {
    { ..merged, inline_size: false }
  } else {
    merged
  }
}

///|
/// Parse container-type into implied contain bits.
/// Supported: normal | size | inline-size
fn parse_container_type(value : String) -> @style.Contain? {
  match value.trim().to_owned() {
    "normal" => Some(@style.Contain::none())
    "size" => Some({ ..@style.Contain::none(), size: true, style: true })
    "inline-size" =>
      Some({ ..@style.Contain::none(), inline_size: true, style: true })
    _ => None
  }
}

// Paint Property Parsing
// =============================================================================

///|
/// Parse visibility property
fn parse_visibility(value : String) -> @style.Visibility {
  match value.trim().to_owned() {
    "visible" => Visible
    "hidden" => Hidden
    "collapse" => Collapse
    _ => Visible
  }
}

///|
fn parse_pointer_events(value : String) -> @style.PointerEvents {
  match value.trim().to_owned() {
    "none" => None
    _ => Auto
  }
}

///|
/// Parse vertical-align property
fn parse_vertical_align(value : String) -> @style.VerticalAlign {
  match value.trim().to_owned() {
    "baseline" => Baseline
    "top" => Top
    "middle" => Middle
    "bottom" => Bottom
    "text-top" => TextTop
    "text-bottom" => TextBottom
    "sub" => Sub
    "super" => Super
    _ => Baseline
  }
}

///|
/// Parse z-index property
fn parse_z_index(value : String) -> @style.ZIndex {
  let trimmed = value.trim().to_owned()
  if trimmed == "auto" {
    Auto
  } else {
    try {
      let v = @string.parse_int(trimmed)
      Value(v)
    } catch {
      _ => Auto
    }
  }
}

///|
/// Parse opacity property (0.0 to 1.0)
fn parse_opacity(value : String) -> Double {
  let trimmed = value.trim().to_owned()
  try {
    let v = @string.parse_double(trimmed)
    // Clamp to valid range
    if v < 0.0 {
      0.0
    } else if v > 1.0 {
      1.0
    } else {
      v
    }
  } catch {
    _ => 1.0
  }
}