///|
/// A contiguous text segment covered by the same font-face index.
struct FontFallbackSpan {
  face_index : Int
  text : String
  char_count : Int
} derive(Debug)

///|
pub extend FontFallbackSpan with Debug::{to_repr}

///|
/// A font fallback plan for a text string.
struct FontFallbackPlan {
  spans : Array[FontFallbackSpan]
  char_count : Int
  missing_char_count : Int
} derive(Debug)

///|
pub extend FontFallbackPlan with Debug::{to_repr}

///|
/// Get the index of the selected face, or -1 when no face covers this span.
pub fn FontFallbackSpan::get_face_index(self : FontFallbackSpan) -> Int {
  self.face_index
}

///|
/// Get the text covered by this span.
pub fn FontFallbackSpan::get_text(self : FontFallbackSpan) -> String {
  self.text
}

///|
/// Get the number of Unicode scalar values in this span.
pub fn FontFallbackSpan::get_char_count(self : FontFallbackSpan) -> Int {
  self.char_count
}

///|
/// Return whether no provided face covers this span.
pub fn FontFallbackSpan::is_missing(self : FontFallbackSpan) -> Bool {
  self.face_index < 0
}

///|
/// Get fallback spans in source-text order.
pub fn FontFallbackPlan::get_spans(
  self : FontFallbackPlan,
) -> Array[FontFallbackSpan] {
  copy_font_fallback_spans(self.spans)
}

///|
/// Get the number of fallback spans.
pub fn FontFallbackPlan::span_count(self : FontFallbackPlan) -> Int {
  self.spans.length()
}

///|
/// Get the number of Unicode scalar values examined by this plan.
pub fn FontFallbackPlan::get_char_count(self : FontFallbackPlan) -> Int {
  self.char_count
}

///|
/// Get the number of scalar values not covered by any provided face.
pub fn FontFallbackPlan::get_missing_char_count(self : FontFallbackPlan) -> Int {
  self.missing_char_count
}

///|
/// Return whether any scalar value lacks glyph coverage.
pub fn FontFallbackPlan::has_missing_glyphs(self : FontFallbackPlan) -> Bool {
  self.missing_char_count > 0
}

///|
/// Return whether the plan has no text spans.
pub fn FontFallbackPlan::is_empty(self : FontFallbackPlan) -> Bool {
  self.spans.length() is 0
}

///|
/// Select the first face that maps a Unicode codepoint to a drawable glyph.
pub fn select_font_face_index_for_char(
  faces : Array[FontFace],
  char_code : Int,
) -> Int {
  for i in 0.. FontFallbackPlan {
  let spans = []
  let mut current_face_index = -2
  let mut current_text = ""
  let mut current_char_count = 0
  let mut char_count = 0
  let mut missing_char_count = 0

  for ch in text {
    let face_index = select_font_face_index_for_char(faces, ch.to_int())
    if face_index < 0 {
      missing_char_count += 1
    }
    if current_char_count > 0 && face_index != current_face_index {
      spans.push(
        make_font_fallback_span(
          current_face_index, current_text, current_char_count,
        ),
      )
      current_text = ""
      current_char_count = 0
    }
    current_face_index = face_index
    current_text = current_text + ch.to_string()
    current_char_count += 1
    char_count += 1
  }

  if current_char_count > 0 {
    spans.push(
      make_font_fallback_span(
        current_face_index, current_text, current_char_count,
      ),
    )
  }

  { spans, char_count, missing_char_count, }
}

///|
fn make_font_fallback_span(
  face_index : Int,
  text : String,
  char_count : Int,
) -> FontFallbackSpan {
  { face_index, text, char_count, }
}

///|
fn copy_font_fallback_spans(
  spans : Array[FontFallbackSpan],
) -> Array[FontFallbackSpan] {
  let copied = []
  for span in spans {
    copied.push(span)
  }
  copied
}