///|
fn extract_bdf_family_name(font_name : String) -> String {
  let len = font_name.length()
  if len == 0 || font_name[0] != '-' {
    return font_name
  }
  let mut field_start = 1
  let mut field_index = 0
  for i in 1.. field_start {
        return font_name[field_start:i].to_string()
      }
      field_index += 1
      field_start = i + 1
    }
  }
  font_name
}

///|
fn bdf_pitch(width : Int, bpp : Int) -> Int {
  if width <= 0 {
    0
  } else {
    (width * bpp + 7) / 8
  }
}

///|
fn bdf_pixel_mode(bpp : Int) -> @types.PixelMode {
  match bpp {
    2 => @types.PixelMode::Gray2
    4 => @types.PixelMode::Gray4
    8 => @types.PixelMode::Gray
    _ => @types.PixelMode::Mono
  }
}

///|
fn bdf_num_grays(bpp : Int) -> UInt {
  match bpp {
    2 => 4U
    4 => 16U
    8 => 256U
    _ => 2U
  }
}

///|
fn make_bdf_bitmap(glyph : @bdf.BdfGlyph, bpp : Int) -> @types.Bitmap {
  let bitmap = @types.Bitmap::new()
  let width = glyph.bbx_width()
  let height = glyph.bbx_height()
  let pitch = bdf_pitch(width, bpp)
  bitmap.set_rows(height.reinterpret_as_uint())
  bitmap.set_width(width.reinterpret_as_uint())
  bitmap.set_pitch(pitch)
  bitmap.set_buffer(glyph.bitmap())
  bitmap.set_num_grays(bdf_num_grays(bpp))
  bitmap.set_pixel_mode(bdf_pixel_mode(bpp))
  bitmap
}

///|
fn glyph_advance_width(glyph : @bdf.BdfGlyph) -> Int {
  let dwidth = glyph.dwidth_x()
  if dwidth != 0 {
    dwidth
  } else {
    glyph.bbx_width()
  }
}

///|
fn bdf_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 bdf_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 = font.charset_registry()
  let encoding = font.charset_encoding()
  if registry == "" || encoding == "" {
    return false
  }
  if bdf_string_starts_with_ignore_ascii_case(registry, "iso") {
    let suffix = registry[3:].to_string()
    return bdf_string_eq_ignore_ascii_case(suffix, "10646") ||
      (
        bdf_string_eq_ignore_ascii_case(suffix, "8859") &&
        bdf_string_eq_ignore_ascii_case(encoding, "1")
      ) ||
      (
        bdf_string_eq_ignore_ascii_case(suffix, "646.1991") &&
        bdf_string_eq_ignore_ascii_case(encoding, "IRV")
      )
  }
  false
}

///|
fn load_bdf(data : Bytes) -> @base.FaceRec raise @error.FTError {
  let font = @bdf.parse_bdf_header(data)
  let face = @base.FaceRec::new()
  let glyphs = font.glyphs()
  let num_chars = font.num_chars()
  let num_glyphs = glyphs.length()
  let family_name = extract_bdf_family_name(font.font_name())
  let mut max_advance_width = font.bbx_width()
  for _, glyph in glyphs {
    max_advance_width = max_advance_width.max(glyph_advance_width(glyph))
  }
  face.set_family_name(family_name)
  face.set_face_flags(@base.FACE_FLAG_FIXED_SIZES | @base.FACE_FLAG_HORIZONTAL)
  let advertised_glyphs = if num_chars > 0 { num_chars } else { num_glyphs }
  face.set_num_glyphs((advertised_glyphs + 1).to_int64())
  face.set_ascender(font.bbx_height() + font.bbx_y_offset())
  face.set_descender(font.bbx_y_offset())
  face.set_height(font.bbx_height())
  face.set_max_advance_width(max_advance_width)
  face.set_max_advance_height(font.bbx_height())
  let strike_ppem = font.point_size().to_int64() << 6
  face.set_available_sizes([
    @base.BitmapSize::new(
      font.bbx_height(),
      font.bbx_width(),
      strike_ppem,
      strike_ppem,
      strike_ppem,
    ),
  ])
  if font.charset_registry() != "" && font.charset_encoding() != "" {
    if bdf_has_unicode_charmap(font) {
      face
      .charmaps()
      .push(@base.CharMapRec::new(@base.Encoding::Unicode, 3U, 1U))
    } else {
      face.charmaps().push(@base.CharMapRec::new(@base.Encoding::None, 0U, 0U))
    }
  } else {
    face
    .charmaps()
    .push(@base.CharMapRec::new(@base.Encoding::AdobeStandard, 7U, 0U))
  }
  face.set_active_charmap(0)

  let cmap : Map[UInt, UInt] = {}
  let mut default_glyph_index = -1
  for i, glyph in glyphs {
    let encoding = glyph.encoding()
    if encoding >= 0 {
      let glyph_id = (i + 1).reinterpret_as_uint()
      cmap[encoding.reinterpret_as_uint()] = glyph_id
      if encoding == font.default_char() {
        default_glyph_index = i
      }
    }
  }
  let bpp = font.bpp()
  let char_index_fn : (UInt) -> UInt = fn(charcode) {
    match cmap.get(charcode) {
      Some(gid) => gid
      None => 0U
    }
  }
  let load_glyph_fn : (UInt, Int) -> @base.BitmapGlyphData raise @error.FTError = fn(
    glyph_index,
    load_flags,
  ) raise @error.FTError {
    ignore(load_flags)
    if glyph_index == 0U {
      if default_glyph_index >= 0 && default_glyph_index < glyphs.length() {
        let glyph = glyphs[default_glyph_index]
        let top = glyph.bbx_height() + glyph.bbx_y_offset()
        let advance = glyph_advance_width(glyph)
        return @base.BitmapGlyphData::new(
          @base.GlyphSlotData::new(
            glyph.bbx_width().to_int64(),
            glyph.bbx_height().to_int64(),
            glyph.bbx_x_offset().to_int64(),
            top.to_int64(),
            advance.to_int64(),
          ),
          make_bdf_bitmap(glyph, bpp),
          glyph.bbx_x_offset(),
          top,
        )
      }
      return @base.BitmapGlyphData::new(
        @base.GlyphSlotData::new(0L, 0L, 0L, 0L, 0L),
        @types.Bitmap::new(),
        0,
        0,
      )
    }
    let index = glyph_index.reinterpret_as_int() - 1
    if index < 0 || index >= glyphs.length() {
      raise @error.FTError::InvalidGlyphIndex
    }
    let glyph = glyphs[index]
    let top = glyph.bbx_height() + glyph.bbx_y_offset()
    let advance = glyph_advance_width(glyph)
    @base.BitmapGlyphData::new(
      @base.GlyphSlotData::new(
        glyph.bbx_width().to_int64(),
        glyph.bbx_height().to_int64(),
        glyph.bbx_x_offset().to_int64(),
        top.to_int64(),
        advance.to_int64(),
      ),
      make_bdf_bitmap(glyph, bpp),
      glyph.bbx_x_offset(),
      top,
    )
  }
  face.set_char_index_fn(char_index_fn)
  face.set_driver_data(
    @base.DriverData::BitmapOps(@base.BitmapOps::new(load_glyph=load_glyph_fn)),
  )
  face
}