// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
/// Minimal layout scaffolding ported from `cosmic-text/src/layout.rs`.
///
/// NOTE: This is an MVP focused on wrap/layout tests; shaping/font fallback will be added later.

///|
/// Metrics hinting strategy
pub(all) enum Hinting {
  Disabled
  Enabled
}

///|
/// Wrapping mode
pub(all) enum Wrap {
  None
  Glyph
  Word
  WordOrGlyph
}

///|
/// Align or justify
pub(all) enum Align {
  Left
  Right
  Center
  Justified
  End
}

///|
/// A laid out glyph (MVP subset)
pub struct LayoutGlyph {
  start : Int
  end : Int
  font_size : Float
  font_id : Int
  font_weight : Int
  glyph_id : Int
  x : Float
  y : Float
  w : Float
  /// BiDi embedding level (LTR if divisible by 2).
  level : Int
  line_height_opt : Float?
  x_offset : Float
  y_offset : Float
  color_opt : Color?
  metadata : Int
  cache_key_flags : CacheKeyFlags
}

///|
/// Convenience constructor (fills optional fields with defaults).
pub fn LayoutGlyph::new(
  start : Int,
  end : Int,
  font_size : Float,
  font_id : Int,
  font_weight : Int,
  glyph_id : Int,
  x : Float,
  y : Float,
  w : Float,
  metadata : Int,
) -> LayoutGlyph {
  LayoutGlyph::{
    start,
    end,
    font_size,
    font_id,
    font_weight,
    glyph_id,
    x,
    y,
    w,
    level: 0,
    line_height_opt: None,
    x_offset: 0.0F,
    y_offset: 0.0F,
    color_opt: None,
    metadata,
    cache_key_flags: 0U,
  }
}

///|
/// A line of laid out glyphs (MVP subset)
pub struct LayoutLine {
  start : Int
  end : Int
  w : Float
  max_ascent : Float
  max_descent : Float
  line_height_opt : Float?
  glyphs : Array[LayoutGlyph]
}

///|
/// A laid out glyph in physical/pixel coordinates, ready for rasterization.
pub struct PhysicalGlyph {
  cache_key : CacheKey
  x : Int
  y : Int
}

///|
pub fn LayoutGlyph::physical(
  self : LayoutGlyph,
  offset : (Float, Float),
  scale : Float,
) -> PhysicalGlyph {
  let x_off = self.font_size * self.x_offset
  let y_off = self.font_size * self.y_offset
  let pos_x = (self.x + x_off) * scale + offset.0
  // Hinting in Y axis (match upstream idea): truncate to integral.
  let pos_y = truncf((self.y - y_off) * scale + offset.1)
  let (cache_key, x, y) = CacheKey::new(
    self.font_id,
    self.glyph_id,
    self.font_size * scale,
    (pos_x, pos_y),
    self.font_weight,
    self.cache_key_flags,
  )
  PhysicalGlyph::{ cache_key, x, y }
}

///|
fn float_from_int(v : Int) -> Float {
  Float::from_double(v.to_double())
}

///|
fn glyph_layout_width(
  glyph : ShapeGlyph,
  default_font_size : Float,
  cell_w : Float,
) -> Float {
  let glyph_font_size = match glyph.metrics_opt {
    Option::None => default_font_size
    Option::Some(m) => m.font_size
  }
  let mut w = glyph.x_advance * glyph_font_size
  if cell_w != default_font_size && default_font_size != 0.0F {
    let match_em_width = cell_w / default_font_size
    if match_em_width != 0.0F {
      // Keep behavior close to upstream `layout_to_buffer`: snap per-glyph advance.
      w = roundf(w / match_em_width) * match_em_width
    }
  }
  w
}

///|
fn max_int(a : Int, b : Int) -> Int {
  if a > b {
    a
  } else {
    b
  }
}

///|

///|
fn prefix_range_w(prefix : Array[Float], start : Int, end : Int) -> Float {
  let s = if start < 0 { 0 } else { start }
  let e = if end < s { s } else { end }
  let a = if prefix.get(s) is Some(v) { v } else { 0.0F }
  let b = if prefix.get(e) is Some(v) { v } else { a }
  b - a
}

///|
priv struct GlyphWrapSegment {
  glyph_start : Int
  glyph_end : Int
  blank : Bool
}

///|
fn layout_is_space_like_char(ch : Char) -> Bool {
  if ch == '\t' {
    return true
  }
  let cat = @moon_swash.CharInfo::from_char(ch).category()
  cat is SpaceSeparator || cat is LineSeparator || cat is ParagraphSeparator
}

///|
fn layout_glyph_is_blank(text : String, glyph : ShapeGlyph) -> Bool {
  let start = if glyph.start < 0 {
    0
  } else if glyph.start > text.length() {
    text.length()
  } else {
    glyph.start
  }
  let end0 = if glyph.end < 0 {
    0
  } else if glyph.end > text.length() {
    text.length()
  } else {
    glyph.end
  }
  let end = if end0 < start { start } else { end0 }
  if end <= start {
    return false
  }
  let run = try! text[start:end]
  let mut has_char = false
  for ch in run {
    has_char = true
    if !layout_is_space_like_char(ch) {
      return false
    }
  }
  has_char
}

///|
fn build_glyph_wrap_segments(
  text : String,
  shape : ShapeLine,
) -> Array[GlyphWrapSegment] {
  let segs : Array[GlyphWrapSegment] = []
  let n = shape.glyphs.length()
  let mut i = 0
  while i < n {
    let blank = layout_glyph_is_blank(text, shape.glyphs[i])
    if blank {
      segs.push(GlyphWrapSegment::{ glyph_start: i, glyph_end: i + 1, blank })
      i = i + 1
      continue
    }
    let start = i
    i = i + 1
    while i < n && !layout_glyph_is_blank(text, shape.glyphs[i]) {
      i = i + 1
    }
    segs.push(GlyphWrapSegment::{ glyph_start: start, glyph_end: i, blank })
  }
  segs
}

///|
fn build_layout_line_from_shape(
  shape : ShapeLine,
  glyph_start : Int,
  glyph_end : Int,
  default_font_size : Float,
  cell_w : Float,
) -> LayoutLine {
  let glyphs : Array[LayoutGlyph] = []
  // Compute the source text span for this visual line from glyph coverage.
  let mut text_start = shape.glyphs[glyph_start].start
  let mut text_end = shape.glyphs[glyph_start].end
  for i in glyph_start.. text_end {
      text_end = g.end
    }
  }
  let mut x = 0.0F
  let mut max_line_height : Float? = Option::None
  let mut max_ascent = 0.0F
  let mut max_descent = 0.0F
  for i in glyph_start.. default_font_size
      Option::Some(m) => m.font_size
    }
    let w = glyph_layout_width(g, default_font_size, cell_w)
    let glyph_line_height_opt = match g.metrics_opt {
      Option::None => Option::None
      Option::Some(m) => Option::Some(m.line_height)
    }
    match (max_line_height, glyph_line_height_opt) {
      (Option::None, Option::Some(h)) => max_line_height = Option::Some(h)
      (Option::Some(a), Option::Some(b)) =>
        if b > a {
          max_line_height = Option::Some(b)
        }
      _ => ()
    }
    let glyph_ascent = glyph_font_size * g.ascent
    if glyph_ascent > max_ascent {
      max_ascent = glyph_ascent
    }
    let glyph_descent = glyph_font_size * g.descent
    if glyph_descent > max_descent {
      max_descent = glyph_descent
    }
    glyphs.push(LayoutGlyph::{
      start: g.start,
      end: g.end,
      font_size: glyph_font_size,
      font_id: g.font_id,
      font_weight: g.font_weight,
      glyph_id: g.glyph_id,
      x,
      y: 0.0F,
      w,
      level: g.level,
      line_height_opt: glyph_line_height_opt,
      x_offset: g.x_offset,
      y_offset: g.y_offset,
      color_opt: None,
      metadata: g.metadata,
      cache_key_flags: 0U,
    })
    x = x + w
  }
  LayoutLine::{
    start: text_start,
    end: text_end,
    w: x,
    max_ascent,
    max_descent,
    line_height_opt: max_line_height,
    glyphs,
  }
}

///|
fn push_layout_line_from_shape(
  lines : Array[LayoutLine],
  shape : ShapeLine,
  glyph_start : Int,
  glyph_end : Int,
  font_size : Float,
  cell_w : Float,
) -> Unit {
  if glyph_end <= glyph_start {
    return
  }
  lines.push(
    build_layout_line_from_shape(
      shape, glyph_start, glyph_end, font_size, cell_w,
    ),
  )
}

///|
fn push_line(
  lines : Array[LayoutLine],
  text_start : Int,
  text_end : Int,
  glyph_w : Float,
) -> Unit {
  let len = text_end - text_start
  let w = glyph_w * float_from_int(len)
  // MVP: 1 glyph per code unit, with x computed from glyph width.
  let glyphs : Array[LayoutGlyph] = []
  for i in 0.. Unit {
  let max_glyphs_f = (max_w / glyph_w).to_double()
  let max_glyphs = max_int(1, max_glyphs_f.to_int())
  let mut i = text_start
  while i < text_end {
    let next = if i + max_glyphs > text_end { text_end } else { i + max_glyphs }
    push_line(lines, i, next, glyph_w)
    i = next
  }
}

///|
/// ASCII-only layout for wrap tests.
///
/// Semantics:
/// - Treat each code unit as one glyph.
/// - `glyph_w` is a monospace advance in pixels.
pub fn layout_ascii(
  text : String,
  glyph_w : Float,
  width_opt : Float?,
  wrap : Wrap,
) -> Array[LayoutLine] {
  let len = text.length()
  let lines : Array[LayoutLine] = []
  if len == 0 {
    return lines
  }
  match wrap {
    None => {
      push_line(lines, 0, len, glyph_w)
      return lines
    }
    Glyph => {
      let max_w = if width_opt is Some(w) { w } else { 0.0F }
      push_line_glyph_chunks(lines, 0, len, glyph_w, max_w)
      return lines
    }
    _ => ()
  }
  if width_opt is None {
    push_line(lines, 0, len, glyph_w)
    return lines
  }
  let max_w = if width_opt is Some(w) { w } else { 0.0F }
  let fallback_to_glyph = match wrap {
    WordOrGlyph => true
    _ => false
  }

  // Word/WordOrGlyph: linebreak-based segments with explicit blank tokens.
  let segs = wrap_word_segments(text)
  let mut i = 0
  while i < segs.length() {
    // Skip leading blank segments.
    while i < segs.length() && segs[i].2 {
      i = i + 1
    }
    if i >= segs.length() {
      break
    }
    let line_start_i = i
    let start_pos = segs[line_start_i].0
    let mut cur_end_pos = start_pos
    let mut last_non_blank_end_pos = start_pos
    let mut emitted = false
    while i < segs.length() {
      let seg = segs[i]
      let blank = seg.2
      let word_w = glyph_w * float_from_int(seg.1 - seg.0)
      let cur_w = glyph_w * float_from_int(cur_end_pos - start_pos)
      let would_w = glyph_w * float_from_int(seg.1 - start_pos)
      let fits = would_w <= max_w || (blank && cur_w <= max_w)
      if fits {
        cur_end_pos = seg.1
        if !blank {
          last_non_blank_end_pos = seg.1
        }
        i = i + 1
        continue
      }

      // Doesn't fit.
      if cur_end_pos == start_pos {
        // Segment doesn't fit on an empty line.
        if fallback_to_glyph && !blank && word_w > max_w {
          push_line_glyph_chunks(lines, seg.0, seg.1, glyph_w, max_w)
          i = i + 1
          emitted = true
          break
        } else {
          // Place it anyway (may overflow), then start new line.
          push_line(lines, seg.0, seg.1, glyph_w)
          i = i + 1
          emitted = true
          break
        }
      }

      // Emit current line without trailing blanks (blank tokens are explicit).
      if last_non_blank_end_pos > start_pos {
        push_line(lines, start_pos, last_non_blank_end_pos, glyph_w)
      }
      emitted = true
      // Start a new line at the current segment (don't consume it).
      break
    }

    // End-of-input: emit remaining line (trim trailing blanks).
    if i >= segs.length() && !emitted && last_non_blank_end_pos > start_pos {
      push_line(lines, start_pos, last_non_blank_end_pos, glyph_w)
    }
  }
  lines
}

///|
fn finalize_layout_lines(
  lines : Array[LayoutLine],
  rtl : Bool,
  width_opt : Float?,
) -> Array[LayoutLine] {
  if !rtl || lines.length() == 0 {
    return lines
  }
  let line_width = match width_opt {
    Some(w) => w
    None => {
      let mut max_w = 0.0F
      for line in lines {
        if line.w > max_w {
          max_w = line.w
        }
      }
      max_w
    }
  }
  let out : Array[LayoutLine] = []
  for line in lines {
    let glyphs : Array[LayoutGlyph] = []
    for glyph in line.glyphs {
      glyphs.push(LayoutGlyph::{ ..glyph, x: line_width - (glyph.x + glyph.w) })
    }
    out.push(LayoutLine::{ ..line, glyphs, })
  }
  out
}

///|
priv struct WrapWordLayout {
  glyph_start : Int
  glyph_end : Int
  blank : Bool
  w : Float
}

///|
priv struct WrapSpanLayout {
  level : Int
  words : Array[WrapWordLayout]
}

///|
priv struct VisualRangeLayout {
  span_index : Int
  start_word : Int
  start_glyph : Int
  end_word : Int
  end_glyph : Int
}

///|
fn build_wrap_spans_from_shape(
  text : String,
  shape : ShapeLine,
  font_size : Float,
  cell_w : Float,
) -> Array[WrapSpanLayout] {
  let spans : Array[WrapSpanLayout] = []
  let n = shape.glyphs.length()
  let mut span_start = 0
  while span_start < n {
    let level = shape.glyphs[span_start].level
    let mut span_end = span_start + 1
    while span_end < n && shape.glyphs[span_end].level == level {
      span_end = span_end + 1
    }
    let words : Array[WrapWordLayout] = []
    let mut i = span_start
    while i < span_end {
      let blank = layout_glyph_is_blank(text, shape.glyphs[i])
      if blank {
        let w = glyph_layout_width(shape.glyphs[i], font_size, cell_w)
        words.push(WrapWordLayout::{
          glyph_start: i,
          glyph_end: i + 1,
          blank: true,
          w,
        })
        i = i + 1
      } else {
        let word_start = i
        let mut w = 0.0F
        while i < span_end && !layout_glyph_is_blank(text, shape.glyphs[i]) {
          w = w + glyph_layout_width(shape.glyphs[i], font_size, cell_w)
          i = i + 1
        }
        words.push(WrapWordLayout::{
          glyph_start: word_start,
          glyph_end: i,
          blank: false,
          w,
        })
      }
    }
    spans.push(WrapSpanLayout::{ level, words })
    span_start = span_end
  }
  spans
}

///|
fn add_visual_range(
  ranges : Array[VisualRangeLayout],
  cur_w : Float,
  span_index : Int,
  start_word : Int,
  start_glyph : Int,
  end_word : Int,
  end_glyph : Int,
  width : Float,
) -> Float {
  if start_word == end_word && start_glyph == end_glyph {
    return cur_w
  }
  ranges.push(VisualRangeLayout::{
    span_index,
    start_word,
    start_glyph,
    end_word,
    end_glyph,
  })
  cur_w + width
}

///|
fn reverse_runs(runs : Array[(Int, Int)], start : Int, end : Int) -> Unit {
  if end <= start {
    return
  }
  let mut i = start
  let mut j = end - 1
  while i < j {
    let t = runs[i]
    runs[i] = runs[j]
    runs[j] = t
    i = i + 1
    j = j - 1
  }
}

///|
fn reorder_visual_range_runs(
  spans : Array[WrapSpanLayout],
  ranges : Array[VisualRangeLayout],
) -> Array[(Int, Int)] {
  let out : Array[(Int, Int)] = []
  if ranges.length() == 0 {
    return out
  }
  let levels : Array[Int] = []
  for r in ranges {
    levels.push(spans[r.span_index].level)
  }
  let mut start = 0
  let mut run_level = levels[0]
  let mut min_level = run_level
  let mut max_level = run_level
  for i in 1.. max_level {
        max_level = run_level
      }
    }
  }
  out.push((start, levels.length()))
  let mut min_odd = min_level
  if min_odd % 2 == 0 {
    min_odd = min_odd + 1
  }
  let mut level = max_level
  while level >= min_odd {
    let mut seq_start = 0
    while seq_start < out.length() {
      let run_start = out[seq_start].0
      if levels[run_start] < level {
        seq_start = seq_start + 1
        continue
      }
      let mut seq_end = seq_start + 1
      while seq_end < out.length() {
        let next_run_start = out[seq_end].0
        if levels[next_run_start] < level {
          break
        }
        seq_end = seq_end + 1
      }
      reverse_runs(out, seq_start, seq_end)
      seq_start = seq_end
    }
    level = level - 1
  }
  out
}

///|
fn build_layout_lines_from_visual_ranges(
  shape : ShapeLine,
  font_size : Float,
  cell_w : Float,
  visual_ranges : Array[Array[VisualRangeLayout]],
  visual_widths : Array[Float],
  spans : Array[WrapSpanLayout],
) -> Array[LayoutLine] {
  let lines : Array[LayoutLine] = []
  for li in 0.. (Float, Int?, Int?, Float?, Float, Float) {
      let mut x2 = x
      let mut ts = text_start
      let mut te = text_end
      let mut max_h = max_line_height
      let mut max_a = max_ascent
      let mut max_d = max_descent
      let vr = ranges[ridx]
      let span = spans[vr.span_index]
      let end_add = if vr.end_glyph == 0 { 0 } else { 1 }
      for wi in vr.start_word..<(vr.end_word + end_add) {
        let word = span.words[wi]
        let word_len = word.glyph_end - word.glyph_start
        let local_start = if wi == vr.start_word { vr.start_glyph } else { 0 }
        let local_end = if wi == vr.end_word { vr.end_glyph } else { word_len }
        for gi in local_start.. font_size
            Some(m) => m.font_size
          }
          let w = glyph_layout_width(g, font_size, cell_w)
          let glyph_line_height_opt : Float? = match g.metrics_opt {
            None => None
            Some(m) => Some(m.line_height)
          }
          match (max_h, glyph_line_height_opt) {
            (None, Some(h)) => max_h = Some(h)
            (Some(a), Some(b)) => if b > a { max_h = Some(b) }
            _ => ()
          }
          let glyph_ascent = glyph_font_size * g.ascent
          if glyph_ascent > max_a {
            max_a = glyph_ascent
          }
          let glyph_descent = glyph_font_size * g.descent
          if glyph_descent > max_d {
            max_d = glyph_descent
          }
          match ts {
            None => ts = Some(g.start)
            Some(v) => if g.start < v { ts = Some(g.start) }
          }
          match te {
            None => te = Some(g.end)
            Some(v) => if g.end > v { te = Some(g.end) }
          }
          glyphs.push(LayoutGlyph::{
            start: g.start,
            end: g.end,
            font_size: glyph_font_size,
            font_id: g.font_id,
            font_weight: g.font_weight,
            glyph_id: g.glyph_id,
            x: x2,
            y: 0.0F,
            w,
            level: g.level,
            line_height_opt: glyph_line_height_opt,
            x_offset: g.x_offset,
            y_offset: g.y_offset,
            color_opt: None,
            metadata: g.metadata,
            cache_key_flags: 0U,
          })
          x2 = x2 + w
        }
      }
      (x2, ts, te, max_h, max_a, max_d)
    }
    if shape.rtl {
      let mut ri = runs.length()
      while ri > 0 {
        let run = runs[ri - 1]
        for ridx in run.0.. Array[LayoutLine] {
  let lines : Array[LayoutLine] = []
  if text.length() == 0 {
    lines.push(LayoutLine::{
      start: 0,
      end: 0,
      w: 0.0F,
      max_ascent: 0.0F,
      max_descent: 0.0F,
      line_height_opt: None,
      glyphs: [],
    })
    return finalize_layout_lines(lines, shape.rtl, width_opt)
  }
  let shape_for_wrap = shape
  let glyph_len = shape_for_wrap.glyphs.length()
  if glyph_len == 0 {
    return finalize_layout_lines(lines, shape_for_wrap.rtl, width_opt)
  }
  match wrap {
    None => {
      let mut line = build_layout_line_from_shape(
        shape_for_wrap, 0, glyph_len, font_size, cell_w,
      )
      let spans = build_wrap_spans_from_shape(
        text, shape_for_wrap, font_size, cell_w,
      )
      let mut total_w = 0.0F
      for span in spans {
        for word in span.words {
          total_w = total_w + word.w
        }
      }
      line = LayoutLine::{ ..line, w: total_w }
      lines.push(line)
      return finalize_layout_lines(lines, shape_for_wrap.rtl, width_opt)
    }
    _ => ()
  }
  match (width_opt, wrap) {
    // No width constraint:
    // - Glyph wrap: no wrapping required, keep full glyph span (includes spaces).
    // - Word/WordOrGlyph: no wrapping required, keep full glyph span (matches upstream behavior).
    (None, Glyph) => {
      push_layout_line_from_shape(
        lines, shape_for_wrap, 0, glyph_len, font_size, cell_w,
      )
      return finalize_layout_lines(lines, shape_for_wrap.rtl, width_opt)
    }
    (None, Word) | (None, WordOrGlyph) => {
      push_layout_line_from_shape(
        lines, shape_for_wrap, 0, glyph_len, font_size, cell_w,
      )
      return finalize_layout_lines(lines, shape_for_wrap.rtl, width_opt)
    }
    _ => ()
  }
  let max_w = if width_opt is Some(w) { w } else { 0.0F }
  match wrap {
    Glyph | Word | WordOrGlyph => {
      let spans = build_wrap_spans_from_shape(
        text, shape_for_wrap, font_size, cell_w,
      )
      let visual_ranges : Array[Array[VisualRangeLayout]] = []
      let visual_widths : Array[Float] = []
      let mut current_ranges : Array[VisualRangeLayout] = []
      let mut current_w = 0.0F
      for span_i in 0.. 0 {
            let i = wi - 1
            let word = span.words[i]
            let word_w = word.w
            let fits = current_w + (word_range_w + word_w) <= max_w ||
              (word.blank && current_w + word_range_w <= max_w)
            if fits {
              if word.blank {
                width_before_last_blank = word_range_w
              }
              word_range_w = word_range_w + word_w
            } else if wrap is Glyph || (wrap is WordOrGlyph && word_w > max_w) {
              if word_range_w > 0.0F && wrap is WordOrGlyph && word_w > max_w {
                current_w = add_visual_range(
                  current_ranges,
                  current_w,
                  span_i,
                  i + 1,
                  0,
                  fitting_word,
                  fitting_glyph,
                  word_range_w,
                )
                if current_ranges.length() > 0 {
                  visual_ranges.push(current_ranges)
                  visual_widths.push(current_w)
                  current_ranges = []
                  current_w = 0.0F
                }
                word_range_w = 0.0F
                fitting_word = i
                fitting_glyph = 0
              }
              let word_len = word.glyph_end - word.glyph_start
              let mut gi = word_len
              while gi > 0 {
                let gidx = gi - 1
                let abs_i = word.glyph_start + gidx
                let glyph_w = glyph_layout_width(
                  shape_for_wrap.glyphs[abs_i],
                  font_size,
                  cell_w,
                )
                if current_w + (word_range_w + glyph_w) <= max_w {
                  word_range_w = word_range_w + glyph_w
                } else {
                  current_w = add_visual_range(
                    current_ranges,
                    current_w,
                    span_i,
                    i,
                    gidx + 1,
                    fitting_word,
                    fitting_glyph,
                    word_range_w,
                  )
                  if current_ranges.length() > 0 {
                    visual_ranges.push(current_ranges)
                    visual_widths.push(current_w)
                    current_ranges = []
                    current_w = 0.0F
                  }
                  word_range_w = glyph_w
                  fitting_word = i
                  fitting_glyph = gidx + 1
                }
                gi = gidx
              }
            } else {
              if word_range_w > 0.0F {
                let trailing_blank = i + 1 < span.words.length() &&
                  span.words[i + 1].blank
                if trailing_blank {
                  current_w = add_visual_range(
                    current_ranges,
                    current_w,
                    span_i,
                    i + 2,
                    0,
                    fitting_word,
                    fitting_glyph,
                    width_before_last_blank,
                  )
                } else {
                  current_w = add_visual_range(
                    current_ranges,
                    current_w,
                    span_i,
                    i + 1,
                    0,
                    fitting_word,
                    fitting_glyph,
                    word_range_w,
                  )
                }
                if current_ranges.length() > 0 {
                  visual_ranges.push(current_ranges)
                  visual_widths.push(current_w)
                  current_ranges = []
                  current_w = 0.0F
                }
              }
              if word.blank {
                word_range_w = 0.0F
                fitting_word = i
                fitting_glyph = 0
              } else {
                word_range_w = word_w
                fitting_word = i + 1
                fitting_glyph = 0
              }
            }
            wi = i
          }
          current_w = add_visual_range(
            current_ranges, current_w, span_i, 0, 0, fitting_word, fitting_glyph,
            word_range_w,
          )
        } else {
          let mut fitting_word = 0
          let mut fitting_glyph = 0
          for i in 0.. max_w) {
              if word_range_w > 0.0F && wrap is WordOrGlyph && word_w > max_w {
                current_w = add_visual_range(
                  current_ranges, current_w, span_i, fitting_word, fitting_glyph,
                  i, 0, word_range_w,
                )
                if current_ranges.length() > 0 {
                  visual_ranges.push(current_ranges)
                  visual_widths.push(current_w)
                  current_ranges = []
                  current_w = 0.0F
                }
                word_range_w = 0.0F
                fitting_word = i
                fitting_glyph = 0
              }
              let word_len = word.glyph_end - word.glyph_start
              for gidx in 0.. 0 {
                    visual_ranges.push(current_ranges)
                    visual_widths.push(current_w)
                    current_ranges = []
                    current_w = 0.0F
                  }
                  word_range_w = glyph_w
                  fitting_word = i
                  fitting_glyph = gidx
                }
              }
            } else {
              if word_range_w > 0.0F {
                let trailing_blank = i > 0 && span.words[i - 1].blank
                if trailing_blank {
                  current_w = add_visual_range(
                    current_ranges,
                    current_w,
                    span_i,
                    fitting_word,
                    fitting_glyph,
                    i - 1,
                    0,
                    width_before_last_blank,
                  )
                } else {
                  current_w = add_visual_range(
                    current_ranges, current_w, span_i, fitting_word, fitting_glyph,
                    i, 0, word_range_w,
                  )
                }
                if current_ranges.length() > 0 {
                  visual_ranges.push(current_ranges)
                  visual_widths.push(current_w)
                  current_ranges = []
                  current_w = 0.0F
                }
              }
              if word.blank {
                word_range_w = 0.0F
                fitting_word = i + 1
                fitting_glyph = 0
              } else {
                word_range_w = word_w
                fitting_word = i
                fitting_glyph = 0
              }
            }
          }
          current_w = add_visual_range(
            current_ranges,
            current_w,
            span_i,
            fitting_word,
            fitting_glyph,
            span.words.length(),
            0,
            word_range_w,
          )
        }
      }
      if current_ranges.length() > 0 {
        visual_ranges.push(current_ranges)
        visual_widths.push(current_w)
      }
      let new_lines = build_layout_lines_from_visual_ranges(
        shape_for_wrap, font_size, cell_w, visual_ranges, visual_widths, spans,
      )
      for line in new_lines {
        lines.push(line)
      }
      return finalize_layout_lines(lines, shape_for_wrap.rtl, width_opt)
    }
    _ => ()
  }
  let fallback_to_glyph = match wrap {
    WordOrGlyph => true
    _ => false
  }

  // Prefix sums of pixel advances for quick range width queries.
  let prefix : Array[Float] = []
  let mut acc = 0.0F
  prefix.push(acc)
  for i in 0..= segs.length() {
      break
    }
    let line_start_i = i
    let mut line_start_g = segs[line_start_i].glyph_start
    let mut cur_end_g = line_start_g
    let mut before_last_blank_end_g = line_start_g
    let mut kept_overflow_after_blank = false
    let mut seen_nonblank = false
    let mut seen_incongruent_nonblank = false
    let mut emitted = false
    // Track last "fitting" glyph end and the end position before the most recent
    // blank segment, mirroring upstream `width_before_last_blank` behavior.
    while i < segs.length() {
      let seg = segs[i]
      let blank = seg.blank
      let would_end_g = seg.glyph_end
      let seg_rtl = shape_for_wrap.glyphs[seg.glyph_start].level % 2 != 0
      let would_w = prefix_range_w(prefix, line_start_g, would_end_g)
      let cur_w = prefix_range_w(prefix, line_start_g, cur_end_g)
      let fits = would_w <= max_w || (blank && cur_w <= max_w)
      if fits {
        if blank {
          before_last_blank_end_g = cur_end_g
        } else if seg_rtl != shape_for_wrap.rtl {
          seen_incongruent_nonblank = true
        }
        if !blank {
          seen_nonblank = true
        }
        cur_end_g = would_end_g
        i = i + 1
        continue
      }

      // Doesn't fit.
      if i == line_start_i {
        // Segment doesn't fit on an empty line.
        let word_start_g = seg.glyph_start
        let word_end_g = seg.glyph_end
        if fallback_to_glyph &&
          prefix_range_w(prefix, word_start_g, word_end_g) > max_w {
          // Match reference behavior: commit wrapped chunks except the final one.
          // Keep the final chunk as the current in-progress line so trailing blanks
          // can still attach before the next wrap decision.
          if seg_rtl != shape_for_wrap.rtl {
            let mut chunk_end = word_end_g
            let mut chunk_w = 0.0F
            let mut gi = word_end_g
            while gi > word_start_g {
              let gidx = gi - 1
              let glyph_w = glyph_layout_width(
                shape_for_wrap.glyphs[gidx],
                font_size,
                cell_w,
              )
              if chunk_w + glyph_w > max_w && gidx + 1 < chunk_end {
                push_layout_line_from_shape(
                  lines,
                  shape_for_wrap,
                  gidx + 1,
                  chunk_end,
                  font_size,
                  cell_w,
                )
                chunk_end = gidx + 1
                chunk_w = 0.0F
              }
              chunk_w = chunk_w + glyph_w
              gi = gidx
            }
            line_start_g = word_start_g
            cur_end_g = chunk_end
          } else {
            let mut chunk_start = word_start_g
            let mut chunk_w = 0.0F
            for gi in word_start_g.. max_w && gi > chunk_start {
                push_layout_line_from_shape(
                  lines, shape_for_wrap, chunk_start, gi, font_size, cell_w,
                )
                chunk_start = gi
                chunk_w = 0.0F
              }
              chunk_w = chunk_w + glyph_w
            }
            line_start_g = chunk_start
            cur_end_g = word_end_g
          }
          before_last_blank_end_g = cur_end_g
          seen_nonblank = true
          i = i + 1
          continue
        } else {
          // Word wrap parity: keep the overflowing word as the in-progress line,
          // and let the next segment decide whether trailing blank should be dropped.
          cur_end_g = word_end_g
          if !blank {
            seen_nonblank = true
          }
          i = i + 1
          continue
        }
      }

      // Reference parity on word-wrap: if a non-blank segment overflows right
      // after a blank separator, keep it on the same line and allow overflow.
      // This matches span-transition behavior in upstream layout.
      if (wrap is Word || wrap is WordOrGlyph) &&
        !blank &&
        i > line_start_i &&
        segs[i - 1].blank &&
        !kept_overflow_after_blank &&
        !seen_incongruent_nonblank &&
        !(wrap is WordOrGlyph &&
        prefix_range_w(prefix, seg.glyph_start, seg.glyph_end) > max_w) &&
        seg_rtl != shape_for_wrap.rtl {
        cur_end_g = would_end_g
        kept_overflow_after_blank = true
        seen_nonblank = true
        seen_incongruent_nonblank = true
        i = i + 1
        continue
      }

      // Emit current line, possibly trimming trailing blanks when wrapping.
      let emit_end = if i > line_start_i && segs[i - 1].blank {
        if wrap is Word {
          before_last_blank_end_g
        } else if blank {
          cur_end_g
        } else if wrap is WordOrGlyph &&
          prefix_range_w(prefix, seg.glyph_start, seg.glyph_end) > max_w {
          cur_end_g
        } else {
          before_last_blank_end_g
        }
      } else {
        cur_end_g
      }
      if emit_end > line_start_g {
        push_layout_line_from_shape(
          lines, shape_for_wrap, line_start_g, emit_end, font_size, cell_w,
        )
      }
      // When a blank segment triggers wrap, consume it so the next line does not
      // start with that overflowing blank (matches upstream word-wrap behavior).
      if blank && (wrap is Word || seen_nonblank) {
        i = i + 1
      }
      emitted = true
      // Start a new line at the current segment (do not consume it).
      break
    }

    // End-of-input: keep trailing blanks that fit in the last visual line.
    if i >= segs.length() && !emitted && cur_end_g > line_start_g {
      push_layout_line_from_shape(
        lines, shape_for_wrap, line_start_g, cur_end_g, font_size, cell_w,
      )
    }
  }
  finalize_layout_lines(lines, shape_for_wrap.rtl, width_opt)
}

///|
fn apply_align_to_line(
  text : String,
  line : LayoutLine,
  width : Float,
  align : Align,
  rtl : Bool,
) -> LayoutLine {
  let extra = width - line.w
  if extra <= 0.0F {
    return line
  }
  match align {
    Left => line
    Center => {
      let dx = extra / 2.0F
      let glyphs : Array[LayoutGlyph] = []
      for g in line.glyphs {
        glyphs.push(LayoutGlyph::{ ..g, x: g.x + dx })
      }
      LayoutLine::{ ..line, glyphs, }
    }
    Right => {
      let dx = extra
      let glyphs : Array[LayoutGlyph] = []
      for g in line.glyphs {
        glyphs.push(LayoutGlyph::{ ..g, x: g.x + dx })
      }
      LayoutLine::{ ..line, glyphs, }
    }
    End =>
      if rtl {
        line
      } else {
        let dx = extra
        let glyphs : Array[LayoutGlyph] = []
        for g in line.glyphs {
          glyphs.push(LayoutGlyph::{ ..g, x: g.x + dx })
        }
        LayoutLine::{ ..line, glyphs, }
      }
    Justified => {
      // MVP: distribute extra width across ASCII spaces in this line.
      let mut space_count = 0
      for i in line.start..= 0 && i < text.length() && text.code_unit_at(i) == 32 {
          space_count = space_count + 1
        }
      }
      if space_count == 0 {
        return line
      }
      let add = extra / Float::from_double(space_count.to_double())
      let glyphs : Array[LayoutGlyph] = []
      let mut dx = 0.0F
      for g in line.glyphs {
        let mut w = g.w
        // Space glyph is identified by [start,end) covering a single code unit space.
        if g.end - g.start == 1 &&
          g.start >= 0 &&
          g.start < text.length() &&
          text.code_unit_at(g.start) == 32 {
          w = w + add
          glyphs.push(LayoutGlyph::{ ..g, x: g.x + dx, w })
          dx = dx + add
        } else {
          glyphs.push(LayoutGlyph::{ ..g, x: g.x + dx, w })
        }
      }
      LayoutLine::{ ..line, w: line.w + extra, glyphs }
    }
  }
}

///|
/// Apply Align to precomputed layout lines (requires a finite width).
pub fn apply_align_with_rtl(
  text : String,
  lines : Array[LayoutLine],
  width : Float,
  align : Align,
  rtl : Bool,
) -> Array[LayoutLine] {
  let out : Array[LayoutLine] = []
  for line in lines {
    out.push(apply_align_to_line(text, line, width, align, rtl))
  }
  out
}

///|
/// Apply Align to precomputed layout lines assuming LTR paragraph direction.
pub fn apply_align(
  text : String,
  lines : Array[LayoutLine],
  width : Float,
  align : Align,
) -> Array[LayoutLine] {
  apply_align_with_rtl(text, lines, width, align, false)
}