///|
/// Emphasis bit of a text span.
pub const STYLE_BOLD : Int32 = 1

///|
/// Emphasis bit of a text span.
pub const STYLE_ITALIC : Int32 = 2

///|
/// Emphasis bit of a text span.
pub const STYLE_MONO : Int32 = 4

///|
/// Emphasis bit of a text span, set for subscript.
pub const STYLE_SUB : Int32 = 8

///|
/// Emphasis bit of a text span, set for superscript.
pub const STYLE_SUP : Int32 = 16

///|
/// Emphasis bit of a text span, set for underlined text.
pub const STYLE_UNDERLINE : Int32 = 32

///|
/// Approximately how much smaller CSS draws text under `font-size="smaller"`.
const SMALLER : Double = 1.2

///|
/// Measures the advance width of text, in pixels.
pub(all) struct TextMeasurer {
  width_of : (String, Double, Int32) -> Double
}

///|
/// The width of `text` at font size `size` with the emphasis bits in `style`.
pub fn TextMeasurer::width(
  self : TextMeasurer,
  text : String,
  size : Double,
  style : Int32,
) -> Double {
  (self.width_of)(text, size, style)
}

///|
/// High surrogates count as wide, so a character outside the BMP takes an em.
fn is_wide(c : Int32) -> Bool {
  (c >= 0x1100 && c <= 0x115f) ||
  (c >= 0x2e80 && c <= 0xa4cf) ||
  (c >= 0xac00 && c <= 0xd7a3) ||
  (c >= 0xf900 && c <= 0xfaff) ||
  (c >= 0xfe30 && c <= 0xfe4f) ||
  (c >= 0xff00 && c <= 0xff60) ||
  (c >= 0xffe0 && c <= 0xffe6) ||
  (c >= 0xd800 && c <= 0xdbff)
}

///|
fn is_zero_width(c : Int32) -> Bool {
  c == 0xad ||
  (c >= 0x300 && c <= 0x36f) ||
  (c >= 0x200b && c <= 0x200f) ||
  (c >= 0xdc00 && c <= 0xdfff) ||
  (c >= 0xfe00 && c <= 0xfe0f) ||
  c == 0xfeff
}

///|
fn metrics_width(text : String, size : Double, style : Int32) -> Double {
  let mono = (style & STYLE_MONO) != 0
  let size = if (style & (STYLE_SUB | STYLE_SUP)) != 0 {
    size / SMALLER
  } else {
    size
  }
  let table = if (style & STYLE_BOLD) != 0 {
    bold_advances
  } else {
    regular_advances
  }
  // pele sums in a JavaScript number, exact for any text a string holds, which an Int32 is not
  // past about two million wide characters.
  let mut units : Int64 = 0
  for i in 0.. AdvanceTable,
  units_per_em : Double,
) -> TextMeasurer {
  let width_of = (text : String, size : Double, style : Int32) => {
    let table = table_of(style)
    let mut units = 0.0
    for i in 0.. units += advance
        None =>
          if is_zero_width(c) {
            continue
          } else if is_wide(c) {
            units += table.wide
          } else {
            units += table.narrow
          }
      }
    }
    units * size / units_per_em
  }
  { width_of, }
}

///|
/// Estimates widths from a table of Arial advances.
/// pele falls back to it where no canvas is available, which is always the case here.
pub let metrics_measurer : TextMeasurer = { width_of: metrics_width, }