///|
/// Parse font-size value and return computed value in pixels
fn parse_font_size(value : String, ctx : ComputeContext) -> Double {
  let v = value.trim()

  // Handle rem units (relative to root font-size) first.
  // "1rem" also ends with "em", so ordering matters.
  match v.strip_suffix("rem") {
    Some(num_str) => {
      let n = @string.parse_double(num_str.to_owned()) catch { _ => 1.0 }
      return n * ctx.root_font_size
    }
    None => ()
  }

  // Handle em units (relative to parent font-size)
  match v.strip_suffix("em") {
    Some(num_str) => {
      let n = @string.parse_double(num_str.to_owned()) catch { _ => 1.0 }
      return n * ctx.font_size
    }
    None => ()
  }

  // Handle px units
  match v.strip_suffix("px") {
    Some(num_str) => {
      let n = @string.parse_double(num_str.to_owned()) catch {
        _ => return ctx.font_size
      }
      return n
    }
    None => ()
  }

  // Handle pt units (1pt = 1.333px)
  match v.strip_suffix("pt") {
    Some(num_str) => {
      let n = @string.parse_double(num_str.to_owned()) catch {
        _ => return ctx.font_size
      }
      return n * 1.333
    }
    None => ()
  }

  // Handle percent (relative to parent font-size)
  match v.strip_suffix("%") {
    Some(num_str) => {
      let n = @string.parse_double(num_str.to_owned()) catch {
        _ => return ctx.font_size
      }
      return n / 100.0 * ctx.font_size
    }
    None => ()
  }

  let plain_number = @string.parse_double(v) catch { _ => -1.0 }
  if plain_number == 0.0 {
    return 0.0
  }

  // Handle keyword values
  match v {
    "xx-small" => 9.0
    "x-small" => 10.0
    "small" => 13.0
    "medium" => 16.0
    "large" => 18.0
    "x-large" => 24.0
    "xx-large" => 32.0
    "smaller" => ctx.font_size * 0.833
    "larger" => ctx.font_size * 1.2
    _ => ctx.font_size // Default to inherited font-size
  }
}

///|
/// Parse line-height value and return computed value in pixels
fn parse_line_height(
  value : String,
  font_size : Double,
  root_font_size? : Double = 16.0,
) -> Double {
  let v = value.trim()

  // Handle unitless number (multiplier of font-size)
  // Check if it's a pure number
  let is_pure_number = {
    let mut pure = true
    for i = 0; i < v.length(); i = i + 1 {
      let c = v[i].to_int().unsafe_to_char()
      if c != '.' && !(c >= '0' && c <= '9') {
        pure = false
        break
      }
    }
    pure
  }
  if is_pure_number && !v.is_empty() {
    let n = @string.parse_double(v.to_owned()) catch { _ => 1.0 }
    return n * font_size
  }

  // Handle px units
  match v.strip_suffix("px") {
    Some(num_str) => {
      let n = @string.parse_double(num_str.to_owned()) catch {
        _ => return font_size
      }
      return n
    }
    None => ()
  }

  // Handle rem units
  match v.strip_suffix("rem") {
    Some(num_str) => {
      let n = @string.parse_double(num_str.to_owned()) catch {
        _ => return font_size
      }
      return n * root_font_size
    }
    None => ()
  }

  // Handle em units
  match v.strip_suffix("em") {
    Some(num_str) => {
      let n = @string.parse_double(num_str.to_owned()) catch {
        _ => return font_size
      }
      return n * font_size
    }
    None => ()
  }

  // Handle percent (relative to font-size)
  match v.strip_suffix("%") {
    Some(num_str) => {
      let n = @string.parse_double(num_str.to_owned()) catch {
        _ => return font_size
      }
      return n / 100.0 * font_size
    }
    None => ()
  }

  // "normal" = 1.2 * font-size typically
  if v == "normal" {
    return font_size * 1.2
  }

  // Default to font-size
  font_size
}

///|
priv struct ParsedFontShorthand {
  font_size : Double
  line_height : Double
  font_weight : Double
  font_family : String
}

///|
/// Parse font shorthand and extract font-size and line-height
/// Supports: [style] [variant] [weight] size[/line-height] family
fn parse_font_shorthand(
  value : String,
  ctx : ComputeContext,
) -> ParsedFontShorthand {
  let v = value.trim()

  // Split by whitespace
  let parts : Array[String] = []
  let mut current = StringBuilder::new()
  let mut in_quotes = false
  for c in v.iter() {
    if c == '"' || c == '\'' {
      in_quotes = !in_quotes
      current.write_char(c)
    } else if (c == ' ' || c == '\t') && !in_quotes {
      let s = current.to_string()
      if s.length() > 0 {
        parts.push(s)
      }
      current = StringBuilder::new()
    } else {
      current.write_char(c)
    }
  }
  let s = current.to_string()
  if s.length() > 0 {
    parts.push(s)
  }

  // Find the part with font-size (and optional /line-height)
  // Font-size is required and comes before font-family
  // Look for a part that starts with a digit or contains 'px', 'em', 'rem', '%'
  let mut font_size = ctx.font_size
  let mut line_height = ctx.font_size
  let mut font_weight = 400.0
  let mut size_index = -1
  for idx = 0; idx < parts.length(); idx = idx + 1 {
    let part = parts[idx]
    // Check if this part contains size/line-height
    if part.contains("/") {
      // Split by /
      let mut slash_idx = -1
      for i = 0; i < part.length(); i = i + 1 {
        if part[i] == '/' {
          slash_idx = i
          break
        }
      }
      if slash_idx > 0 {
        let size_part = part.unsafe_substring(start=0, end=slash_idx)
        let lh_part = part.unsafe_substring(
          start=slash_idx + 1,
          end=part.length(),
        )
        font_size = parse_font_size(size_part, ctx)
        line_height = parse_line_height(
          lh_part,
          font_size,
          root_font_size=ctx.root_font_size,
        )
        size_index = idx
        break
      }
    } else {
      // Check if it's a size value (starts with digit or contains unit)
      let first_char = if part.length() > 0 { part[0] } else { ' ' }
      if first_char >= '0' && first_char <= '9' {
        font_size = parse_font_size(part, ctx)
        line_height = font_size // Default line-height = font-size (ratio 1)
        size_index = idx
        break
      }
    }
  }
  if size_index > 0 {
    for idx = 0; idx < size_index; idx = idx + 1 {
      let part = parts[idx].trim().to_lower().to_owned()
      if part == "normal" ||
        part == "bold" ||
        part == "bolder" ||
        part == "lighter" {
        font_weight = parse_font_weight(part)
        continue
      }
      let parsed_weight = @string.parse_double(part) catch { _ => -1.0 }
      if parsed_weight >= 1.0 && parsed_weight <= 1000.0 {
        font_weight = parsed_weight
      }
    }
  }
  let font_family = if size_index >= 0 && size_index + 1 < parts.length() {
    let family_parts : Array[String] = []
    for idx = size_index + 1; idx < parts.length(); idx = idx + 1 {
      family_parts.push(parts[idx])
    }
    parse_font_family(family_parts.join(" "))
  } else {
    ""
  }
  { font_size, line_height, font_weight, font_family }
}