// THIS FILE IS AUTOGENERATED.
// Any changes to this file will be overwritten.
// For more information about how codegen works, see font-codegen/README.md

#[allow(unused_imports)]
use crate::codegen_prelude::*;

impl<'a> MinByteRange<'a> for CffHeader<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.trailing_data_byte_range().end
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        let range = self.min_byte_range();
        self.data.as_bytes().get(range).unwrap_or_default()
    }
}

impl<'a> FontRead<'a> for CffHeader<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        #[allow(clippy::absurd_extreme_comparisons)]
        if data.len() < Self::MIN_SIZE {
            return Err(ReadError::OutOfBounds);
        }
        Ok(Self { data })
    }
}

/// [Compact Font Format](https://learn.microsoft.com/en-us/typography/opentype/spec/cff) table header
#[derive(Clone)]
pub struct CffHeader<'a> {
    data: FontData<'a>,
}

#[allow(clippy::needless_lifetimes)]
impl<'a> CffHeader<'a> {
    pub const MIN_SIZE: usize =
        (u8::RAW_BYTE_LEN + u8::RAW_BYTE_LEN + u8::RAW_BYTE_LEN + u8::RAW_BYTE_LEN);
    basic_table_impls!(impl_the_methods);

    /// Format major version (starting at 1).
    pub fn major(&self) -> u8 {
        let range = self.major_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Format minor version (starting at 0).
    pub fn minor(&self) -> u8 {
        let range = self.minor_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Header size (bytes).
    pub fn hdr_size(&self) -> u8 {
        let range = self.hdr_size_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Absolute offset size.
    pub fn off_size(&self) -> u8 {
        let range = self.off_size_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Padding bytes before the start of the Name INDEX.
    pub fn _padding(&self) -> &'a [u8] {
        let range = self._padding_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

    /// Remaining table data.
    pub fn trailing_data(&self) -> &'a [u8] {
        let range = self.trailing_data_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

    pub fn major_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + u8::RAW_BYTE_LEN
    }

    pub fn minor_byte_range(&self) -> Range<usize> {
        let start = self.major_byte_range().end;
        start..start + u8::RAW_BYTE_LEN
    }

    pub fn hdr_size_byte_range(&self) -> Range<usize> {
        let start = self.minor_byte_range().end;
        start..start + u8::RAW_BYTE_LEN
    }

    pub fn off_size_byte_range(&self) -> Range<usize> {
        let start = self.hdr_size_byte_range().end;
        start..start + u8::RAW_BYTE_LEN
    }

    pub fn _padding_byte_range(&self) -> Range<usize> {
        let hdr_size = self.hdr_size();
        let start = self.off_size_byte_range().end;
        start..start + (transforms::subtract(hdr_size, 4_usize)).saturating_mul(u8::RAW_BYTE_LEN)
    }

    pub fn trailing_data_byte_range(&self) -> Range<usize> {
        let start = self._padding_byte_range().end;
        start..start + self.data.len().saturating_sub(start) / u8::RAW_BYTE_LEN * u8::RAW_BYTE_LEN
    }
}

const _: () = assert!(FontData::default_data_long_enough(CffHeader::MIN_SIZE));

impl Default for CffHeader<'_> {
    fn default() -> Self {
        Self {
            data: FontData::default_table_data(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for CffHeader<'a> {
    fn type_name(&self) -> &str {
        "CffHeader"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("major", self.major())),
            1usize => Some(Field::new("minor", self.minor())),
            2usize => Some(Field::new("hdr_size", self.hdr_size())),
            3usize => Some(Field::new("off_size", self.off_size())),
            4usize => Some(Field::new("_padding", self._padding())),
            5usize => Some(Field::new("trailing_data", self.trailing_data())),
            _ => None,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
#[allow(clippy::needless_lifetimes)]
impl<'a> std::fmt::Debug for CffHeader<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (self as &dyn SomeTable<'a>).fmt(f)
    }
}

impl<'a> MinByteRange<'a> for Index<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.data_byte_range().end
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        let range = self.min_byte_range();
        self.data.as_bytes().get(range).unwrap_or_default()
    }
}

impl<'a> FontRead<'a> for Index<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        #[allow(clippy::absurd_extreme_comparisons)]
        if data.len() < Self::MIN_SIZE {
            return Err(ReadError::OutOfBounds);
        }
        Ok(Self { data })
    }
}

/// An array of variable-sized objects in a `CFF` table.
#[derive(Clone)]
pub struct Index<'a> {
    data: FontData<'a>,
}

#[allow(clippy::needless_lifetimes)]
impl<'a> Index<'a> {
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u8::RAW_BYTE_LEN);
    basic_table_impls!(impl_the_methods);

    /// Number of objects stored in INDEX.
    pub fn count(&self) -> u16 {
        let range = self.count_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Object array element size.
    pub fn off_size(&self) -> u8 {
        let range = self.off_size_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Bytes containing `count + 1` offsets each of `off_size`.
    pub fn offsets(&self) -> &'a [u8] {
        let range = self.offsets_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

    /// Array containing the object data.
    pub fn data(&self) -> &'a [u8] {
        let range = self.data_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

    pub fn count_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + u16::RAW_BYTE_LEN
    }

    pub fn off_size_byte_range(&self) -> Range<usize> {
        let start = self.count_byte_range().end;
        start..start + u8::RAW_BYTE_LEN
    }

    pub fn offsets_byte_range(&self) -> Range<usize> {
        let count = self.count();
        let off_size = self.off_size();
        let start = self.off_size_byte_range().end;
        start
            ..start
                + (transforms::add_multiply(count, 1_usize, off_size))
                    .saturating_mul(u8::RAW_BYTE_LEN)
    }

    pub fn data_byte_range(&self) -> Range<usize> {
        let start = self.offsets_byte_range().end;
        start..start + self.data.len().saturating_sub(start) / u8::RAW_BYTE_LEN * u8::RAW_BYTE_LEN
    }
}

const _: () = assert!(FontData::default_data_long_enough(Index::MIN_SIZE));

impl Default for Index<'_> {
    fn default() -> Self {
        Self {
            data: FontData::default_table_data(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for Index<'a> {
    fn type_name(&self) -> &str {
        "Index"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("count", self.count())),
            1usize => Some(Field::new("off_size", self.off_size())),
            2usize => Some(Field::new("offsets", self.offsets())),
            3usize => Some(Field::new("data", self.data())),
            _ => None,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
#[allow(clippy::needless_lifetimes)]
impl<'a> std::fmt::Debug for Index<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (self as &dyn SomeTable<'a>).fmt(f)
    }
}

/// Associates a glyph identifier with a Font DICT.
#[derive(Clone)]
pub enum FdSelect<'a> {
    Format0(FdSelectFormat0<'a>),
    Format3(FdSelectFormat3<'a>),
    Format4(FdSelectFormat4<'a>),
}

impl Default for FdSelect<'_> {
    fn default() -> Self {
        Self::Format0(Default::default())
    }
}

impl<'a> FdSelect<'a> {
    ///Return the `FontData` used to resolve offsets for this table.
    pub fn offset_data(&self) -> FontData<'a> {
        match self {
            Self::Format0(item) => item.offset_data(),
            Self::Format3(item) => item.offset_data(),
            Self::Format4(item) => item.offset_data(),
        }
    }

    /// Format = 0.
    pub fn format(&self) -> u8 {
        match self {
            Self::Format0(item) => item.format(),
            Self::Format3(item) => item.format(),
            Self::Format4(item) => item.format(),
        }
    }
}

impl<'a> FontRead<'a> for FdSelect<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        let format: u8 = data.read_at(0usize)?;
        match format {
            FdSelectFormat0::FORMAT => Ok(Self::Format0(FontRead::read(data)?)),
            FdSelectFormat3::FORMAT => Ok(Self::Format3(FontRead::read(data)?)),
            FdSelectFormat4::FORMAT => Ok(Self::Format4(FontRead::read(data)?)),
            other => Err(ReadError::InvalidFormat(other.into())),
        }
    }
}

impl<'a> MinByteRange<'a> for FdSelect<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        match self {
            Self::Format0(item) => item.min_byte_range(),
            Self::Format3(item) => item.min_byte_range(),
            Self::Format4(item) => item.min_byte_range(),
        }
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        match self {
            Self::Format0(item) => item.min_table_bytes(),
            Self::Format3(item) => item.min_table_bytes(),
            Self::Format4(item) => item.min_table_bytes(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> FdSelect<'a> {
    fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
        match self {
            Self::Format0(table) => table,
            Self::Format3(table) => table,
            Self::Format4(table) => table,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl std::fmt::Debug for FdSelect<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.dyn_inner().fmt(f)
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for FdSelect<'a> {
    fn type_name(&self) -> &str {
        self.dyn_inner().type_name()
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        self.dyn_inner().get_field(idx)
    }
}

impl Format<u8> for FdSelectFormat0<'_> {
    const FORMAT: u8 = 0;
}

impl<'a> MinByteRange<'a> for FdSelectFormat0<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.fds_byte_range().end
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        let range = self.min_byte_range();
        self.data.as_bytes().get(range).unwrap_or_default()
    }
}

impl<'a> FontRead<'a> for FdSelectFormat0<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        #[allow(clippy::absurd_extreme_comparisons)]
        if data.len() < Self::MIN_SIZE {
            return Err(ReadError::OutOfBounds);
        }
        Ok(Self { data })
    }
}

/// FdSelect format 0.
#[derive(Clone)]
pub struct FdSelectFormat0<'a> {
    data: FontData<'a>,
}

#[allow(clippy::needless_lifetimes)]
impl<'a> FdSelectFormat0<'a> {
    pub const MIN_SIZE: usize = u8::RAW_BYTE_LEN;
    basic_table_impls!(impl_the_methods);

    /// Format = 0.
    pub fn format(&self) -> u8 {
        let range = self.format_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// FD selector array (one entry for each glyph).
    pub fn fds(&self) -> &'a [u8] {
        let range = self.fds_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

    pub fn format_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + u8::RAW_BYTE_LEN
    }

    pub fn fds_byte_range(&self) -> Range<usize> {
        let start = self.format_byte_range().end;
        start..start + self.data.len().saturating_sub(start) / u8::RAW_BYTE_LEN * u8::RAW_BYTE_LEN
    }
}

const _: () = assert!(FontData::default_data_long_enough(
    FdSelectFormat0::MIN_SIZE
));

impl Default for FdSelectFormat0<'_> {
    fn default() -> Self {
        Self {
            data: FontData::default_table_data(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for FdSelectFormat0<'a> {
    fn type_name(&self) -> &str {
        "FdSelectFormat0"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("format", self.format())),
            1usize => Some(Field::new("fds", self.fds())),
            _ => None,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
#[allow(clippy::needless_lifetimes)]
impl<'a> std::fmt::Debug for FdSelectFormat0<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (self as &dyn SomeTable<'a>).fmt(f)
    }
}

impl Format<u8> for FdSelectFormat3<'_> {
    const FORMAT: u8 = 3;
}

impl<'a> MinByteRange<'a> for FdSelectFormat3<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.sentinel_byte_range().end
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        let range = self.min_byte_range();
        self.data.as_bytes().get(range).unwrap_or_default()
    }
}

impl<'a> FontRead<'a> for FdSelectFormat3<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        #[allow(clippy::absurd_extreme_comparisons)]
        if data.len() < Self::MIN_SIZE {
            return Err(ReadError::OutOfBounds);
        }
        Ok(Self { data })
    }
}

/// FdSelect format 3.
#[derive(Clone)]
pub struct FdSelectFormat3<'a> {
    data: FontData<'a>,
}

#[allow(clippy::needless_lifetimes)]
impl<'a> FdSelectFormat3<'a> {
    pub const MIN_SIZE: usize = (u8::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
    basic_table_impls!(impl_the_methods);

    /// Format = 3.
    pub fn format(&self) -> u8 {
        let range = self.format_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Number of ranges.
    pub fn n_ranges(&self) -> u16 {
        let range = self.n_ranges_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Range3 array.
    pub fn ranges(&self) -> &'a [FdSelectRange3] {
        let range = self.ranges_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

    /// Sentinel GID. Set equal to the number of glyphs in the font.
    pub fn sentinel(&self) -> u16 {
        let range = self.sentinel_byte_range();
        self.data.read_at(range.start).ok().unwrap_or_default()
    }

    pub fn format_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + u8::RAW_BYTE_LEN
    }

    pub fn n_ranges_byte_range(&self) -> Range<usize> {
        let start = self.format_byte_range().end;
        start..start + u16::RAW_BYTE_LEN
    }

    pub fn ranges_byte_range(&self) -> Range<usize> {
        let n_ranges = self.n_ranges();
        let start = self.n_ranges_byte_range().end;
        start..start + (n_ranges as usize).saturating_mul(FdSelectRange3::RAW_BYTE_LEN)
    }

    pub fn sentinel_byte_range(&self) -> Range<usize> {
        let start = self.ranges_byte_range().end;
        start..start + u16::RAW_BYTE_LEN
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for FdSelectFormat3<'a> {
    fn type_name(&self) -> &str {
        "FdSelectFormat3"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("format", self.format())),
            1usize => Some(Field::new("n_ranges", self.n_ranges())),
            2usize => Some(Field::new(
                "ranges",
                traversal::FieldType::array_of_records(
                    stringify!(FdSelectRange3),
                    self.ranges(),
                    self.offset_data(),
                ),
            )),
            3usize => Some(Field::new("sentinel", self.sentinel())),
            _ => None,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
#[allow(clippy::needless_lifetimes)]
impl<'a> std::fmt::Debug for FdSelectFormat3<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (self as &dyn SomeTable<'a>).fmt(f)
    }
}

/// Range struct for FdSelect format 3.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
#[repr(C)]
#[repr(packed)]
pub struct FdSelectRange3 {
    /// First glyph index in range.
    pub first: BigEndian<u16>,
    /// FD index for all glyphs in range.
    pub fd: u8,
}

impl FdSelectRange3 {
    /// First glyph index in range.
    pub fn first(&self) -> u16 {
        self.first.get()
    }

    /// FD index for all glyphs in range.
    pub fn fd(&self) -> u8 {
        self.fd
    }
}

impl FixedSize for FdSelectRange3 {
    const RAW_BYTE_LEN: usize = u16::RAW_BYTE_LEN + u8::RAW_BYTE_LEN;
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeRecord<'a> for FdSelectRange3 {
    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
        RecordResolver {
            name: "FdSelectRange3",
            get_field: Box::new(move |idx, _data| match idx {
                0usize => Some(Field::new("first", self.first())),
                1usize => Some(Field::new("fd", self.fd())),
                _ => None,
            }),
            data,
        }
    }
}

impl Format<u8> for FdSelectFormat4<'_> {
    const FORMAT: u8 = 4;
}

impl<'a> MinByteRange<'a> for FdSelectFormat4<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.sentinel_byte_range().end
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        let range = self.min_byte_range();
        self.data.as_bytes().get(range).unwrap_or_default()
    }
}

impl<'a> FontRead<'a> for FdSelectFormat4<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        #[allow(clippy::absurd_extreme_comparisons)]
        if data.len() < Self::MIN_SIZE {
            return Err(ReadError::OutOfBounds);
        }
        Ok(Self { data })
    }
}

/// FdSelect format 4.
#[derive(Clone)]
pub struct FdSelectFormat4<'a> {
    data: FontData<'a>,
}

#[allow(clippy::needless_lifetimes)]
impl<'a> FdSelectFormat4<'a> {
    pub const MIN_SIZE: usize = (u8::RAW_BYTE_LEN + u32::RAW_BYTE_LEN + u32::RAW_BYTE_LEN);
    basic_table_impls!(impl_the_methods);

    /// Format = 4.
    pub fn format(&self) -> u8 {
        let range = self.format_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Number of ranges.
    pub fn n_ranges(&self) -> u32 {
        let range = self.n_ranges_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Range4 array.
    pub fn ranges(&self) -> &'a [FdSelectRange4] {
        let range = self.ranges_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

    /// Sentinel GID. Set equal to the number of glyphs in the font.
    pub fn sentinel(&self) -> u32 {
        let range = self.sentinel_byte_range();
        self.data.read_at(range.start).ok().unwrap_or_default()
    }

    pub fn format_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + u8::RAW_BYTE_LEN
    }

    pub fn n_ranges_byte_range(&self) -> Range<usize> {
        let start = self.format_byte_range().end;
        start..start + u32::RAW_BYTE_LEN
    }

    pub fn ranges_byte_range(&self) -> Range<usize> {
        let n_ranges = self.n_ranges();
        let start = self.n_ranges_byte_range().end;
        start..start + (n_ranges as usize).saturating_mul(FdSelectRange4::RAW_BYTE_LEN)
    }

    pub fn sentinel_byte_range(&self) -> Range<usize> {
        let start = self.ranges_byte_range().end;
        start..start + u32::RAW_BYTE_LEN
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for FdSelectFormat4<'a> {
    fn type_name(&self) -> &str {
        "FdSelectFormat4"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("format", self.format())),
            1usize => Some(Field::new("n_ranges", self.n_ranges())),
            2usize => Some(Field::new(
                "ranges",
                traversal::FieldType::array_of_records(
                    stringify!(FdSelectRange4),
                    self.ranges(),
                    self.offset_data(),
                ),
            )),
            3usize => Some(Field::new("sentinel", self.sentinel())),
            _ => None,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
#[allow(clippy::needless_lifetimes)]
impl<'a> std::fmt::Debug for FdSelectFormat4<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (self as &dyn SomeTable<'a>).fmt(f)
    }
}

/// Range struct for FdSelect format 4.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
#[repr(C)]
#[repr(packed)]
pub struct FdSelectRange4 {
    /// First glyph index in range.
    pub first: BigEndian<u32>,
    /// FD index for all glyphs in range.
    pub fd: BigEndian<u16>,
}

impl FdSelectRange4 {
    /// First glyph index in range.
    pub fn first(&self) -> u32 {
        self.first.get()
    }

    /// FD index for all glyphs in range.
    pub fn fd(&self) -> u16 {
        self.fd.get()
    }
}

impl FixedSize for FdSelectRange4 {
    const RAW_BYTE_LEN: usize = u32::RAW_BYTE_LEN + u16::RAW_BYTE_LEN;
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeRecord<'a> for FdSelectRange4 {
    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
        RecordResolver {
            name: "FdSelectRange4",
            get_field: Box::new(move |idx, _data| match idx {
                0usize => Some(Field::new("first", self.first())),
                1usize => Some(Field::new("fd", self.fd())),
                _ => None,
            }),
            data,
        }
    }
}

/// Charset with custom glyph id to string id mappings.
#[derive(Clone)]
pub enum CustomCharset<'a> {
    Format0(CharsetFormat0<'a>),
    Format1(CharsetFormat1<'a>),
    Format2(CharsetFormat2<'a>),
}

impl Default for CustomCharset<'_> {
    fn default() -> Self {
        Self::Format0(Default::default())
    }
}

impl<'a> CustomCharset<'a> {
    ///Return the `FontData` used to resolve offsets for this table.
    pub fn offset_data(&self) -> FontData<'a> {
        match self {
            Self::Format0(item) => item.offset_data(),
            Self::Format1(item) => item.offset_data(),
            Self::Format2(item) => item.offset_data(),
        }
    }

    /// Format; =0
    pub fn format(&self) -> u8 {
        match self {
            Self::Format0(item) => item.format(),
            Self::Format1(item) => item.format(),
            Self::Format2(item) => item.format(),
        }
    }
}

impl<'a> FontRead<'a> for CustomCharset<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        let format: u8 = data.read_at(0usize)?;
        match format {
            CharsetFormat0::FORMAT => Ok(Self::Format0(FontRead::read(data)?)),
            CharsetFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
            CharsetFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
            other => Err(ReadError::InvalidFormat(other.into())),
        }
    }
}

impl<'a> MinByteRange<'a> for CustomCharset<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        match self {
            Self::Format0(item) => item.min_byte_range(),
            Self::Format1(item) => item.min_byte_range(),
            Self::Format2(item) => item.min_byte_range(),
        }
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        match self {
            Self::Format0(item) => item.min_table_bytes(),
            Self::Format1(item) => item.min_table_bytes(),
            Self::Format2(item) => item.min_table_bytes(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> CustomCharset<'a> {
    fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
        match self {
            Self::Format0(table) => table,
            Self::Format1(table) => table,
            Self::Format2(table) => table,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl std::fmt::Debug for CustomCharset<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.dyn_inner().fmt(f)
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for CustomCharset<'a> {
    fn type_name(&self) -> &str {
        self.dyn_inner().type_name()
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        self.dyn_inner().get_field(idx)
    }
}

impl Format<u8> for CharsetFormat0<'_> {
    const FORMAT: u8 = 0;
}

impl<'a> MinByteRange<'a> for CharsetFormat0<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.glyph_byte_range().end
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        let range = self.min_byte_range();
        self.data.as_bytes().get(range).unwrap_or_default()
    }
}

impl<'a> FontRead<'a> for CharsetFormat0<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        #[allow(clippy::absurd_extreme_comparisons)]
        if data.len() < Self::MIN_SIZE {
            return Err(ReadError::OutOfBounds);
        }
        Ok(Self { data })
    }
}

/// Charset format 0.
#[derive(Clone)]
pub struct CharsetFormat0<'a> {
    data: FontData<'a>,
}

#[allow(clippy::needless_lifetimes)]
impl<'a> CharsetFormat0<'a> {
    pub const MIN_SIZE: usize = u8::RAW_BYTE_LEN;
    basic_table_impls!(impl_the_methods);

    /// Format; =0
    pub fn format(&self) -> u8 {
        let range = self.format_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Glyph name array.
    pub fn glyph(&self) -> &'a [BigEndian<u16>] {
        let range = self.glyph_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

    pub fn format_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + u8::RAW_BYTE_LEN
    }

    pub fn glyph_byte_range(&self) -> Range<usize> {
        let start = self.format_byte_range().end;
        start..start + self.data.len().saturating_sub(start) / u16::RAW_BYTE_LEN * u16::RAW_BYTE_LEN
    }
}

const _: () = assert!(FontData::default_data_long_enough(CharsetFormat0::MIN_SIZE));

impl Default for CharsetFormat0<'_> {
    fn default() -> Self {
        Self {
            data: FontData::default_table_data(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for CharsetFormat0<'a> {
    fn type_name(&self) -> &str {
        "CharsetFormat0"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("format", self.format())),
            1usize => Some(Field::new("glyph", self.glyph())),
            _ => None,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
#[allow(clippy::needless_lifetimes)]
impl<'a> std::fmt::Debug for CharsetFormat0<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (self as &dyn SomeTable<'a>).fmt(f)
    }
}

impl Format<u8> for CharsetFormat1<'_> {
    const FORMAT: u8 = 1;
}

impl<'a> MinByteRange<'a> for CharsetFormat1<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.ranges_byte_range().end
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        let range = self.min_byte_range();
        self.data.as_bytes().get(range).unwrap_or_default()
    }
}

impl<'a> FontRead<'a> for CharsetFormat1<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        #[allow(clippy::absurd_extreme_comparisons)]
        if data.len() < Self::MIN_SIZE {
            return Err(ReadError::OutOfBounds);
        }
        Ok(Self { data })
    }
}

/// Charset format 1.
#[derive(Clone)]
pub struct CharsetFormat1<'a> {
    data: FontData<'a>,
}

#[allow(clippy::needless_lifetimes)]
impl<'a> CharsetFormat1<'a> {
    pub const MIN_SIZE: usize = u8::RAW_BYTE_LEN;
    basic_table_impls!(impl_the_methods);

    /// Format; =1
    pub fn format(&self) -> u8 {
        let range = self.format_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Range1 array.
    pub fn ranges(&self) -> &'a [CharsetRange1] {
        let range = self.ranges_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

    pub fn format_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + u8::RAW_BYTE_LEN
    }

    pub fn ranges_byte_range(&self) -> Range<usize> {
        let start = self.format_byte_range().end;
        start
            ..start
                + self.data.len().saturating_sub(start) / CharsetRange1::RAW_BYTE_LEN
                    * CharsetRange1::RAW_BYTE_LEN
    }
}

const _: () = assert!(FontData::default_data_long_enough(CharsetFormat1::MIN_SIZE));

impl Default for CharsetFormat1<'_> {
    fn default() -> Self {
        Self {
            data: FontData::default_format_1_u8_table_data(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for CharsetFormat1<'a> {
    fn type_name(&self) -> &str {
        "CharsetFormat1"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("format", self.format())),
            1usize => Some(Field::new(
                "ranges",
                traversal::FieldType::array_of_records(
                    stringify!(CharsetRange1),
                    self.ranges(),
                    self.offset_data(),
                ),
            )),
            _ => None,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
#[allow(clippy::needless_lifetimes)]
impl<'a> std::fmt::Debug for CharsetFormat1<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (self as &dyn SomeTable<'a>).fmt(f)
    }
}

/// Range struct for Charset format 1.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
#[repr(C)]
#[repr(packed)]
pub struct CharsetRange1 {
    /// First glyph in range.
    pub first: BigEndian<u16>,
    /// Glyphs left in range (excluding first).
    pub n_left: u8,
}

impl CharsetRange1 {
    /// First glyph in range.
    pub fn first(&self) -> u16 {
        self.first.get()
    }

    /// Glyphs left in range (excluding first).
    pub fn n_left(&self) -> u8 {
        self.n_left
    }
}

impl FixedSize for CharsetRange1 {
    const RAW_BYTE_LEN: usize = u16::RAW_BYTE_LEN + u8::RAW_BYTE_LEN;
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeRecord<'a> for CharsetRange1 {
    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
        RecordResolver {
            name: "CharsetRange1",
            get_field: Box::new(move |idx, _data| match idx {
                0usize => Some(Field::new("first", self.first())),
                1usize => Some(Field::new("n_left", self.n_left())),
                _ => None,
            }),
            data,
        }
    }
}

impl Format<u8> for CharsetFormat2<'_> {
    const FORMAT: u8 = 2;
}

impl<'a> MinByteRange<'a> for CharsetFormat2<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.ranges_byte_range().end
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        let range = self.min_byte_range();
        self.data.as_bytes().get(range).unwrap_or_default()
    }
}

impl<'a> FontRead<'a> for CharsetFormat2<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        #[allow(clippy::absurd_extreme_comparisons)]
        if data.len() < Self::MIN_SIZE {
            return Err(ReadError::OutOfBounds);
        }
        Ok(Self { data })
    }
}

/// Charset format 2.
#[derive(Clone)]
pub struct CharsetFormat2<'a> {
    data: FontData<'a>,
}

#[allow(clippy::needless_lifetimes)]
impl<'a> CharsetFormat2<'a> {
    pub const MIN_SIZE: usize = u8::RAW_BYTE_LEN;
    basic_table_impls!(impl_the_methods);

    /// Format; =2
    pub fn format(&self) -> u8 {
        let range = self.format_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Range2 array.
    pub fn ranges(&self) -> &'a [CharsetRange2] {
        let range = self.ranges_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

    pub fn format_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + u8::RAW_BYTE_LEN
    }

    pub fn ranges_byte_range(&self) -> Range<usize> {
        let start = self.format_byte_range().end;
        start
            ..start
                + self.data.len().saturating_sub(start) / CharsetRange2::RAW_BYTE_LEN
                    * CharsetRange2::RAW_BYTE_LEN
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for CharsetFormat2<'a> {
    fn type_name(&self) -> &str {
        "CharsetFormat2"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("format", self.format())),
            1usize => Some(Field::new(
                "ranges",
                traversal::FieldType::array_of_records(
                    stringify!(CharsetRange2),
                    self.ranges(),
                    self.offset_data(),
                ),
            )),
            _ => None,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
#[allow(clippy::needless_lifetimes)]
impl<'a> std::fmt::Debug for CharsetFormat2<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (self as &dyn SomeTable<'a>).fmt(f)
    }
}

/// Range struct for Charset format 2.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
#[repr(C)]
#[repr(packed)]
pub struct CharsetRange2 {
    /// First glyph in range.
    pub first: BigEndian<u16>,
    /// Glyphs left in range (excluding first).
    pub n_left: BigEndian<u16>,
}

impl CharsetRange2 {
    /// First glyph in range.
    pub fn first(&self) -> u16 {
        self.first.get()
    }

    /// Glyphs left in range (excluding first).
    pub fn n_left(&self) -> u16 {
        self.n_left.get()
    }
}

impl FixedSize for CharsetRange2 {
    const RAW_BYTE_LEN: usize = u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN;
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeRecord<'a> for CharsetRange2 {
    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
        RecordResolver {
            name: "CharsetRange2",
            get_field: Box::new(move |idx, _data| match idx {
                0usize => Some(Field::new("first", self.first())),
                1usize => Some(Field::new("n_left", self.n_left())),
                _ => None,
            }),
            data,
        }
    }
}

/// Range struct for Encoding format 1.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
#[repr(C)]
#[repr(packed)]
pub struct EncodingRange1 {
    /// First code in range.
    pub first: u8,
    /// Codes left in range (excluding first).
    pub n_left: u8,
}

impl EncodingRange1 {
    /// First code in range.
    pub fn first(&self) -> u8 {
        self.first
    }

    /// Codes left in range (excluding first).
    pub fn n_left(&self) -> u8 {
        self.n_left
    }
}

impl FixedSize for EncodingRange1 {
    const RAW_BYTE_LEN: usize = u8::RAW_BYTE_LEN + u8::RAW_BYTE_LEN;
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeRecord<'a> for EncodingRange1 {
    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
        RecordResolver {
            name: "EncodingRange1",
            get_field: Box::new(move |idx, _data| match idx {
                0usize => Some(Field::new("first", self.first())),
                1usize => Some(Field::new("n_left", self.n_left())),
                _ => None,
            }),
            data,
        }
    }
}

/// Supplemental encoding record.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
#[repr(C)]
#[repr(packed)]
pub struct EncodingSupplement {
    /// Encoding.
    pub code: u8,
    /// Name.
    pub glyph: BigEndian<u16>,
}

impl EncodingSupplement {
    /// Encoding.
    pub fn code(&self) -> u8 {
        self.code
    }

    /// Name.
    pub fn glyph(&self) -> u16 {
        self.glyph.get()
    }
}

impl FixedSize for EncodingSupplement {
    const RAW_BYTE_LEN: usize = u8::RAW_BYTE_LEN + u16::RAW_BYTE_LEN;
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeRecord<'a> for EncodingSupplement {
    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
        RecordResolver {
            name: "EncodingSupplement",
            get_field: Box::new(move |idx, _data| match idx {
                0usize => Some(Field::new("code", self.code())),
                1usize => Some(Field::new("glyph", self.glyph())),
                _ => None,
            }),
            data,
        }
    }
}
