///|
/// Portable text measurement used by the built-in SVG renderers.
pub struct TextMetrics {
  width : Int
  advance_width : Int
  height : Int
  ascent : Int
  descent : Int
  baseline : Int
  ink_left : Int
  ink_right : Int
} derive(Eq, Debug)

///|
pub extend TextMetrics with Eq::{not_equal, equal}

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

///|
/// A copy whose advance is widened by `extra` pixels, for renderers that
/// compensate the built-in width table (e.g. the mindmap's space advance).
pub fn TextMetrics::with_extra_advance(
  self : TextMetrics,
  extra : Int,
) -> TextMetrics {
  {
    ..self,
    width: self.width + extra,
    advance_width: self.advance_width + extra,
  }
}

///|
/// Coarse built-in width profiles for deterministic cross-platform layout.
pub(all) enum FontProfile {
  Sans
  Serif
  Monospace
} derive(Eq, Debug)

///|
pub extend FontProfile with Eq::{not_equal, equal}

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

///|
pub(all) enum FontStyle {
  Normal
  Italic
} derive(Eq, Debug)

///|
pub extend FontStyle with Eq::{not_equal, equal}

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

///|
/// Configurable, deterministic font metrics for SVG layout.
///
/// Glyph advances are accumulated internally at 1/1000px precision. The
/// public width fields are rounded pixel summaries retained for callers that
/// inspect individual character classes.
pub struct FontMetrics {
  profile : FontProfile
  style : FontStyle
  font_size : Int
  font_weight : Int
  line_height : Int
  ascent : Int
  descent : Int
  line_gap : Int
  letter_spacing : Int
  word_spacing : Int
  tab_size : Int
  kerning_scale : Int
  font_stretch : Int
  italic_width_scale : Int
  italic_overhang : Int
  width_scale : Int
  uppercase_width_scale : Int
  lowercase_width_scale : Int
  digit_width_scale : Int
  punctuation_width_scale : Int
  wide_width_scale : Int
  emoji_width_scale : Int
  bold_width_scale : Int
  space_width : Int
  narrow_width : Int
  normal_width : Int
  wide_width : Int
  uppercase_width : Int
  digit_width : Int
  cjk_width : Int
  emoji_width : Int
} derive(Eq, Debug)

///|
pub extend FontMetrics with Eq::{not_equal, equal}

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

///|
pub impl Default for FontMetrics with fn default() -> FontMetrics {
  FontMetrics::from_defaults()
}

///|
pub extend FontMetrics with Default::{default}

///|
/// Creates deterministic metrics from one of the built-in profiles.
/// Percentage parameters use 100 as their neutral value.
pub fn FontMetrics::from_defaults(
  profile? : FontProfile = Sans,
  style? : FontStyle = Normal,
  font_size? : Int = 13,
  font_weight? : Int = 400,
  line_height? : Int = 0,
  ascent? : Int = 0,
  descent? : Int = 0,
  line_gap? : Int = 0,
  width_scale? : Int = 100,
  uppercase_width_scale? : Int = 100,
  lowercase_width_scale? : Int = 100,
  digit_width_scale? : Int = 100,
  punctuation_width_scale? : Int = 100,
  wide_width_scale? : Int = 100,
  emoji_width_scale? : Int = 100,
  font_stretch? : Int = 100,
  bold_width_scale? : Int = 110,
  italic_width_scale? : Int = 103,
  italic_overhang? : Int = 1,
  kerning_scale? : Int = 100,
  letter_spacing? : Int = 0,
  word_spacing? : Int = 0,
  tab_size? : Int = 4,
) -> FontMetrics {
  let font_size = font_size.max(1)
  let ascent = if ascent > 0 { ascent } else { font_size * 10 / 13 }
  let descent = if descent > 0 { descent } else { font_size * 3 / 13 }
  let line_gap = if line_gap > 0 { line_gap } else { font_size * 2 / 13 }
  let line_height = {
    guard line_height > 0 else { ascent + descent + line_gap }
    line_height
  }
  let metrics = {
    profile,
    style,
    font_size,
    font_weight,
    line_height,
    ascent,
    descent,
    line_gap,
    letter_spacing,
    word_spacing,
    tab_size: tab_size.max(1),
    kerning_scale,
    font_stretch,
    italic_width_scale,
    italic_overhang,
    width_scale,
    uppercase_width_scale,
    lowercase_width_scale,
    digit_width_scale,
    punctuation_width_scale,
    wide_width_scale,
    emoji_width_scale,
    bold_width_scale,
    space_width: 0,
    narrow_width: 0,
    normal_width: 0,
    wide_width: 0,
    uppercase_width: 0,
    digit_width: 0,
    cjk_width: 0,
    emoji_width: 0,
  }
  {
    ..metrics,
    space_width: metrics.char_advance(' ').ceil_pixels(),
    narrow_width: metrics.char_advance('i').ceil_pixels(),
    normal_width: metrics.char_advance('a').ceil_pixels(),
    wide_width: metrics.char_advance('m').ceil_pixels(),
    uppercase_width: metrics.char_advance('A').ceil_pixels(),
    digit_width: metrics.char_advance('0').ceil_pixels(),
    cjk_width: metrics.char_advance('\u{4e00}').ceil_pixels(),
    emoji_width: metrics.char_advance('\u{1f600}').ceil_pixels(),
  }
}