///|
fn ascii_lower_code(unit : UInt16) -> UInt16 {
  let code = unit.to_int()
  if code >= 'A'.to_int() && code <= 'Z'.to_int() {
    (code + 32).to_uint16()
  } else {
    unit
  }
}

///|
fn string_eq_ignore_ascii_case(left : String, right : String) -> Bool {
  if left.length() != right.length() {
    return false
  }
  for i in 0.. Bool {
  if prefix.length() > text.length() {
    return false
  }
  for i in 0.. Bool {
  let registry = match font.property_string("CHARSET_REGISTRY") {
    Some(value) => value
    None => return false
  }
  let encoding = font.property_string("CHARSET_ENCODING").unwrap_or("")
  if string_starts_with_ignore_ascii_case(registry, "iso") {
    let suffix = registry[3:].to_string()
    return string_eq_ignore_ascii_case(suffix, "10646") ||
      (
        string_eq_ignore_ascii_case(suffix, "8859") &&
        string_eq_ignore_ascii_case(encoding, "1")
      ) ||
      (
        string_eq_ignore_ascii_case(suffix, "646.1991") &&
        string_eq_ignore_ascii_case(encoding, "IRV")
      )
  }
  false
}

///|
fn pcf_pitch(width : Int, format : UInt) -> Int {
  match 1 << (format & 3U).reinterpret_as_int() {
    1 => (width + 7) >> 3
    2 => (width + 15) >> 4 << 1
    4 => (width + 31) >> 5 << 2
    8 => (width + 63) >> 6 << 3
    _ => 0
  }
}

///|
fn build_pcf_bitmap(
  metric : @pcf.PcfMetric,
  bitmap_data : Bytes,
  format : UInt,
) -> @types.Bitmap {
  let bitmap = @types.Bitmap::new()
  let width = metric.right_bearing() - metric.left_bearing()
  let rows = metric.ascent() + metric.descent()
  bitmap.set_rows(if rows > 0 { rows.reinterpret_as_uint() } else { 0U })
  bitmap.set_width(if width > 0 { width.reinterpret_as_uint() } else { 0U })
  bitmap.set_pitch(pcf_pitch(width, format))
  bitmap.set_buffer(bitmap_data)
  bitmap.set_num_grays(1U)
  bitmap.set_pixel_mode(@types.PixelMode::Mono)
  bitmap
}

///|
fn load_pcf(data : Bytes) -> @base.FaceRec raise @error.FTError {
  let font = @pcf.parse_pcf_font(data)
  let face = @base.FaceRec::new()
  let family_name = match font.property_string("FAMILY_NAME") {
    Some(name) if name != "" => name
    _ => extract_bdf_family_name(font.font_name())
  }
  face.set_family_name(family_name)
  let style_name = font.property_string("WEIGHT_NAME").unwrap_or("")
  if style_name != "" {
    face.set_style_name(style_name)
  }
  let mut max_width = 0
  let mut max_rows = 0
  let mut first_advance : Int? = None
  let mut fixed_width = true
  for raw_index in 0.. {
        let width = metric.right_bearing() - metric.left_bearing()
        let rows = metric.ascent() + metric.descent()
        if metric.width() > max_width {
          max_width = metric.width()
        }
        if rows > max_rows {
          max_rows = rows
        }
        match first_advance {
          Some(advance) if advance != metric.width() => fixed_width = false
          None => first_advance = Some(metric.width())
          _ => ()
        }
        ignore(width)
      }
      None => ()
    }
  }
  let spacing = font.property_string("SPACING").unwrap_or("")
  if string_eq_ignore_ascii_case(spacing, "M") ||
    string_eq_ignore_ascii_case(spacing, "C") {
    fixed_width = true
  }
  let mut face_flags = @base.FACE_FLAG_FIXED_SIZES | @base.FACE_FLAG_HORIZONTAL
  if fixed_width {
    face_flags = face_flags | @base.FACE_FLAG_FIXED_WIDTH
  }
  face.set_face_flags(face_flags)
  face.set_num_glyphs((font.num_glyphs() + 1).to_int64())
  let font_ascent = font.property_int("FONT_ASCENT").unwrap_or(max_rows)
  let font_descent = font.property_int("FONT_DESCENT").unwrap_or(0)
  let pixel_size = font
    .property_int("PIXEL_SIZE")
    .unwrap_or(font_ascent + font_descent)
  let point_size = font.property_int("POINT_SIZE").unwrap_or(pixel_size * 10)
  let strike_ppem = if point_size > 0 {
    ((point_size + 5) / 10).to_int64() << 6
  } else {
    pixel_size.to_int64() << 6
  }
  face.set_ascender(font_ascent)
  face.set_descender(-font_descent)
  face.set_height(font_ascent + font_descent)
  face.set_max_advance_width(max_width)
  face.set_max_advance_height(font_ascent + font_descent)
  face.set_available_sizes([
    @base.BitmapSize::new(
      pixel_size, max_width, strike_ppem, strike_ppem, strike_ppem,
    ),
  ])
  if is_pcf_unicode(font) {
    face.charmaps().push(@base.CharMapRec::new(@base.Encoding::Unicode, 3U, 1U))
  } else {
    face.charmaps().push(@base.CharMapRec::new(@base.Encoding::None, 0U, 0U))
  }
  face.set_active_charmap(0)
  let char_index_fn : (UInt) -> UInt = fn(charcode) {
    let raw_index = font.glyph_index(charcode.reinterpret_as_int())
    if raw_index >= 0 {
      raw_index.reinterpret_as_uint() + 1U
    } else {
      0U
    }
  }
  let default_raw_index = font.default_glyph_index()
  let load_glyph_fn : (UInt, Int) -> @base.BitmapGlyphData raise @error.FTError = fn(
    glyph_index,
    load_flags,
  ) raise @error.FTError {
    ignore(load_flags)
    let raw_index = if glyph_index == 0U {
      default_raw_index
    } else {
      glyph_index.reinterpret_as_int() - 1
    }
    match font.metric(raw_index) {
      Some(metric) => {
        let width = metric.right_bearing() - metric.left_bearing()
        let rows = metric.ascent() + metric.descent()
        let slot_data = @base.GlyphSlotData::new(
          width.to_int64(),
          rows.to_int64(),
          metric.left_bearing().to_int64(),
          metric.ascent().to_int64(),
          metric.width().to_int64(),
        )
        @base.BitmapGlyphData::new(
          slot_data,
          build_pcf_bitmap(
            metric,
            font.bitmap_for_glyph(raw_index),
            font.bitmaps_format(),
          ),
          metric.left_bearing(),
          metric.ascent(),
        )
      }
      None => raise @error.FTError::InvalidGlyphIndex
    }
  }
  face.set_char_index_fn(char_index_fn)
  face.set_driver_data(
    @base.DriverData::BitmapOps(@base.BitmapOps::new(load_glyph=load_glyph_fn)),
  )
  face
}