// 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::*;

/// [GSUB](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#gsub-header)
#[derive(Debug, Clone, Copy)]
#[doc(hidden)]
pub struct GsubMarker {
    feature_variations_offset_byte_start: Option<usize>,
}

impl GsubMarker {
    pub fn version_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + MajorMinor::RAW_BYTE_LEN
    }

    pub fn script_list_offset_byte_range(&self) -> Range<usize> {
        let start = self.version_byte_range().end;
        start..start + Offset16::RAW_BYTE_LEN
    }

    pub fn feature_list_offset_byte_range(&self) -> Range<usize> {
        let start = self.script_list_offset_byte_range().end;
        start..start + Offset16::RAW_BYTE_LEN
    }

    pub fn lookup_list_offset_byte_range(&self) -> Range<usize> {
        let start = self.feature_list_offset_byte_range().end;
        start..start + Offset16::RAW_BYTE_LEN
    }

    pub fn feature_variations_offset_byte_range(&self) -> Option<Range<usize>> {
        let start = self.feature_variations_offset_byte_start?;
        Some(start..start + Offset32::RAW_BYTE_LEN)
    }
}

impl MinByteRange for GsubMarker {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.lookup_list_offset_byte_range().end
    }
}

impl TopLevelTable for Gsub<'_> {
    /// `GSUB`
    const TAG: Tag = Tag::new(b"GSUB");
}

impl<'a> FontRead<'a> for Gsub<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        let mut cursor = data.cursor();
        let version: MajorMinor = cursor.read()?;
        cursor.advance::<Offset16>();
        cursor.advance::<Offset16>();
        cursor.advance::<Offset16>();
        let feature_variations_offset_byte_start = version
            .compatible((1u16, 1u16))
            .then(|| cursor.position())
            .transpose()?;
        version
            .compatible((1u16, 1u16))
            .then(|| cursor.advance::<Offset32>());
        cursor.finish(GsubMarker {
            feature_variations_offset_byte_start,
        })
    }
}

/// [GSUB](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#gsub-header)
pub type Gsub<'a> = TableRef<'a, GsubMarker>;

#[allow(clippy::needless_lifetimes)]
impl<'a> Gsub<'a> {
    /// The major and minor version of the GSUB table, as a tuple (u16, u16)
    pub fn version(&self) -> MajorMinor {
        let range = self.shape.version_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Offset to ScriptList table, from beginning of GSUB table
    pub fn script_list_offset(&self) -> Offset16 {
        let range = self.shape.script_list_offset_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Attempt to resolve [`script_list_offset`][Self::script_list_offset].
    pub fn script_list(&self) -> Result<ScriptList<'a>, ReadError> {
        let data = self.data;
        self.script_list_offset().resolve(data)
    }

    /// Offset to FeatureList table, from beginning of GSUB table
    pub fn feature_list_offset(&self) -> Offset16 {
        let range = self.shape.feature_list_offset_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Attempt to resolve [`feature_list_offset`][Self::feature_list_offset].
    pub fn feature_list(&self) -> Result<FeatureList<'a>, ReadError> {
        let data = self.data;
        self.feature_list_offset().resolve(data)
    }

    /// Offset to LookupList table, from beginning of GSUB table
    pub fn lookup_list_offset(&self) -> Offset16 {
        let range = self.shape.lookup_list_offset_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Attempt to resolve [`lookup_list_offset`][Self::lookup_list_offset].
    pub fn lookup_list(&self) -> Result<SubstitutionLookupList<'a>, ReadError> {
        let data = self.data;
        self.lookup_list_offset().resolve(data)
    }

    /// Offset to FeatureVariations table, from beginning of the GSUB
    /// table (may be NULL)
    pub fn feature_variations_offset(&self) -> Option<Nullable<Offset32>> {
        let range = self.shape.feature_variations_offset_byte_range()?;
        Some(self.data.read_at(range.start).unwrap())
    }

    /// Attempt to resolve [`feature_variations_offset`][Self::feature_variations_offset].
    pub fn feature_variations(&self) -> Option<Result<FeatureVariations<'a>, ReadError>> {
        let data = self.data;
        self.feature_variations_offset().map(|x| x.resolve(data))?
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for Gsub<'a> {
    fn type_name(&self) -> &str {
        "Gsub"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        let version = self.version();
        match idx {
            0usize => Some(Field::new("version", self.version())),
            1usize => Some(Field::new(
                "script_list_offset",
                FieldType::offset(self.script_list_offset(), self.script_list()),
            )),
            2usize => Some(Field::new(
                "feature_list_offset",
                FieldType::offset(self.feature_list_offset(), self.feature_list()),
            )),
            3usize => Some(Field::new(
                "lookup_list_offset",
                FieldType::offset(self.lookup_list_offset(), self.lookup_list()),
            )),
            4usize if version.compatible((1u16, 1u16)) => Some(Field::new(
                "feature_variations_offset",
                FieldType::offset(
                    self.feature_variations_offset().unwrap(),
                    self.feature_variations(),
                ),
            )),
            _ => None,
        }
    }
}

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

/// A [GSUB Lookup](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#gsubLookupTypeEnum) subtable.
pub enum SubstitutionLookup<'a> {
    Single(Lookup<'a, SingleSubst<'a>>),
    Multiple(Lookup<'a, MultipleSubstFormat1<'a>>),
    Alternate(Lookup<'a, AlternateSubstFormat1<'a>>),
    Ligature(Lookup<'a, LigatureSubstFormat1<'a>>),
    Contextual(Lookup<'a, SubstitutionSequenceContext<'a>>),
    ChainContextual(Lookup<'a, SubstitutionChainContext<'a>>),
    Extension(Lookup<'a, ExtensionSubtable<'a>>),
    Reverse(Lookup<'a, ReverseChainSingleSubstFormat1<'a>>),
}

impl<'a> FontRead<'a> for SubstitutionLookup<'a> {
    fn read(bytes: FontData<'a>) -> Result<Self, ReadError> {
        let untyped = Lookup::read(bytes)?;
        match untyped.lookup_type() {
            1 => Ok(SubstitutionLookup::Single(untyped.into_concrete())),
            2 => Ok(SubstitutionLookup::Multiple(untyped.into_concrete())),
            3 => Ok(SubstitutionLookup::Alternate(untyped.into_concrete())),
            4 => Ok(SubstitutionLookup::Ligature(untyped.into_concrete())),
            5 => Ok(SubstitutionLookup::Contextual(untyped.into_concrete())),
            6 => Ok(SubstitutionLookup::ChainContextual(untyped.into_concrete())),
            7 => Ok(SubstitutionLookup::Extension(untyped.into_concrete())),
            8 => Ok(SubstitutionLookup::Reverse(untyped.into_concrete())),
            other => Err(ReadError::InvalidFormat(other.into())),
        }
    }
}

impl<'a> SubstitutionLookup<'a> {
    #[allow(dead_code)]
    /// Return the inner table, removing the specific generics.
    ///
    /// This lets us return a single concrete type we can call methods on.
    pub(crate) fn of_unit_type(&self) -> Lookup<'a, ()> {
        match self {
            SubstitutionLookup::Single(inner) => inner.of_unit_type(),
            SubstitutionLookup::Multiple(inner) => inner.of_unit_type(),
            SubstitutionLookup::Alternate(inner) => inner.of_unit_type(),
            SubstitutionLookup::Ligature(inner) => inner.of_unit_type(),
            SubstitutionLookup::Contextual(inner) => inner.of_unit_type(),
            SubstitutionLookup::ChainContextual(inner) => inner.of_unit_type(),
            SubstitutionLookup::Extension(inner) => inner.of_unit_type(),
            SubstitutionLookup::Reverse(inner) => inner.of_unit_type(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SubstitutionLookup<'a> {
    fn dyn_inner(&self) -> &(dyn SomeTable<'a> + 'a) {
        match self {
            SubstitutionLookup::Single(table) => table,
            SubstitutionLookup::Multiple(table) => table,
            SubstitutionLookup::Alternate(table) => table,
            SubstitutionLookup::Ligature(table) => table,
            SubstitutionLookup::Contextual(table) => table,
            SubstitutionLookup::ChainContextual(table) => table,
            SubstitutionLookup::Extension(table) => table,
            SubstitutionLookup::Reverse(table) => table,
        }
    }
}

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

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

/// LookupType 1: [Single Substitution](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#lookuptype-1-single-substitution-subtable) Subtable
#[derive(Clone)]
pub enum SingleSubst<'a> {
    Format1(SingleSubstFormat1<'a>),
    Format2(SingleSubstFormat2<'a>),
}

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

    /// Format identifier: format = 1
    pub fn subst_format(&self) -> u16 {
        match self {
            Self::Format1(item) => item.subst_format(),
            Self::Format2(item) => item.subst_format(),
        }
    }

    /// Offset to Coverage table, from beginning of substitution
    /// subtable
    pub fn coverage_offset(&self) -> Offset16 {
        match self {
            Self::Format1(item) => item.coverage_offset(),
            Self::Format2(item) => item.coverage_offset(),
        }
    }
}

impl<'a> FontRead<'a> for SingleSubst<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        let format: u16 = data.read_at(0usize)?;
        match format {
            SingleSubstFormat1Marker::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
            SingleSubstFormat2Marker::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
            other => Err(ReadError::InvalidFormat(other.into())),
        }
    }
}

impl MinByteRange for SingleSubst<'_> {
    fn min_byte_range(&self) -> Range<usize> {
        match self {
            Self::Format1(item) => item.min_byte_range(),
            Self::Format2(item) => item.min_byte_range(),
        }
    }
}

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

#[cfg(feature = "experimental_traverse")]
impl std::fmt::Debug for SingleSubst<'_> {
    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 SingleSubst<'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<u16> for SingleSubstFormat1Marker {
    const FORMAT: u16 = 1;
}

/// [Single Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#11-single-substitution-format-1)
#[derive(Debug, Clone, Copy)]
#[doc(hidden)]
pub struct SingleSubstFormat1Marker {}

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

    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
        let start = self.subst_format_byte_range().end;
        start..start + Offset16::RAW_BYTE_LEN
    }

    pub fn delta_glyph_id_byte_range(&self) -> Range<usize> {
        let start = self.coverage_offset_byte_range().end;
        start..start + i16::RAW_BYTE_LEN
    }
}

impl MinByteRange for SingleSubstFormat1Marker {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.delta_glyph_id_byte_range().end
    }
}

impl<'a> FontRead<'a> for SingleSubstFormat1<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        let mut cursor = data.cursor();
        cursor.advance::<u16>();
        cursor.advance::<Offset16>();
        cursor.advance::<i16>();
        cursor.finish(SingleSubstFormat1Marker {})
    }
}

/// [Single Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#11-single-substitution-format-1)
pub type SingleSubstFormat1<'a> = TableRef<'a, SingleSubstFormat1Marker>;

#[allow(clippy::needless_lifetimes)]
impl<'a> SingleSubstFormat1<'a> {
    /// Format identifier: format = 1
    pub fn subst_format(&self) -> u16 {
        let range = self.shape.subst_format_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Offset to Coverage table, from beginning of substitution
    /// subtable
    pub fn coverage_offset(&self) -> Offset16 {
        let range = self.shape.coverage_offset_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
        let data = self.data;
        self.coverage_offset().resolve(data)
    }

    /// Add to original glyph ID to get substitute glyph ID
    pub fn delta_glyph_id(&self) -> i16 {
        let range = self.shape.delta_glyph_id_byte_range();
        self.data.read_at(range.start).unwrap()
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for SingleSubstFormat1<'a> {
    fn type_name(&self) -> &str {
        "SingleSubstFormat1"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("subst_format", self.subst_format())),
            1usize => Some(Field::new(
                "coverage_offset",
                FieldType::offset(self.coverage_offset(), self.coverage()),
            )),
            2usize => Some(Field::new("delta_glyph_id", self.delta_glyph_id())),
            _ => None,
        }
    }
}

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

impl Format<u16> for SingleSubstFormat2Marker {
    const FORMAT: u16 = 2;
}

/// [Single Substitution Format 2](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#12-single-substitution-format-2)
#[derive(Debug, Clone, Copy)]
#[doc(hidden)]
pub struct SingleSubstFormat2Marker {
    substitute_glyph_ids_byte_len: usize,
}

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

    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
        let start = self.subst_format_byte_range().end;
        start..start + Offset16::RAW_BYTE_LEN
    }

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

    pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
        let start = self.glyph_count_byte_range().end;
        start..start + self.substitute_glyph_ids_byte_len
    }
}

impl MinByteRange for SingleSubstFormat2Marker {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.substitute_glyph_ids_byte_range().end
    }
}

impl<'a> FontRead<'a> for SingleSubstFormat2<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        let mut cursor = data.cursor();
        cursor.advance::<u16>();
        cursor.advance::<Offset16>();
        let glyph_count: u16 = cursor.read()?;
        let substitute_glyph_ids_byte_len = (glyph_count as usize)
            .checked_mul(GlyphId16::RAW_BYTE_LEN)
            .ok_or(ReadError::OutOfBounds)?;
        cursor.advance_by(substitute_glyph_ids_byte_len);
        cursor.finish(SingleSubstFormat2Marker {
            substitute_glyph_ids_byte_len,
        })
    }
}

/// [Single Substitution Format 2](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#12-single-substitution-format-2)
pub type SingleSubstFormat2<'a> = TableRef<'a, SingleSubstFormat2Marker>;

#[allow(clippy::needless_lifetimes)]
impl<'a> SingleSubstFormat2<'a> {
    /// Format identifier: format = 2
    pub fn subst_format(&self) -> u16 {
        let range = self.shape.subst_format_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Offset to Coverage table, from beginning of substitution
    /// subtable
    pub fn coverage_offset(&self) -> Offset16 {
        let range = self.shape.coverage_offset_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
        let data = self.data;
        self.coverage_offset().resolve(data)
    }

    /// Number of glyph IDs in the substituteGlyphIDs array
    pub fn glyph_count(&self) -> u16 {
        let range = self.shape.glyph_count_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Array of substitute glyph IDs — ordered by Coverage index
    pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
        let range = self.shape.substitute_glyph_ids_byte_range();
        self.data.read_array(range).unwrap()
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for SingleSubstFormat2<'a> {
    fn type_name(&self) -> &str {
        "SingleSubstFormat2"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("subst_format", self.subst_format())),
            1usize => Some(Field::new(
                "coverage_offset",
                FieldType::offset(self.coverage_offset(), self.coverage()),
            )),
            2usize => Some(Field::new("glyph_count", self.glyph_count())),
            3usize => Some(Field::new(
                "substitute_glyph_ids",
                self.substitute_glyph_ids(),
            )),
            _ => None,
        }
    }
}

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

impl Format<u16> for MultipleSubstFormat1Marker {
    const FORMAT: u16 = 1;
}

/// [Multiple Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#21-multiple-substitution-format-1)
#[derive(Debug, Clone, Copy)]
#[doc(hidden)]
pub struct MultipleSubstFormat1Marker {
    sequence_offsets_byte_len: usize,
}

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

    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
        let start = self.subst_format_byte_range().end;
        start..start + Offset16::RAW_BYTE_LEN
    }

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

    pub fn sequence_offsets_byte_range(&self) -> Range<usize> {
        let start = self.sequence_count_byte_range().end;
        start..start + self.sequence_offsets_byte_len
    }
}

impl MinByteRange for MultipleSubstFormat1Marker {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.sequence_offsets_byte_range().end
    }
}

impl<'a> FontRead<'a> for MultipleSubstFormat1<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        let mut cursor = data.cursor();
        cursor.advance::<u16>();
        cursor.advance::<Offset16>();
        let sequence_count: u16 = cursor.read()?;
        let sequence_offsets_byte_len = (sequence_count as usize)
            .checked_mul(Offset16::RAW_BYTE_LEN)
            .ok_or(ReadError::OutOfBounds)?;
        cursor.advance_by(sequence_offsets_byte_len);
        cursor.finish(MultipleSubstFormat1Marker {
            sequence_offsets_byte_len,
        })
    }
}

/// [Multiple Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#21-multiple-substitution-format-1)
pub type MultipleSubstFormat1<'a> = TableRef<'a, MultipleSubstFormat1Marker>;

#[allow(clippy::needless_lifetimes)]
impl<'a> MultipleSubstFormat1<'a> {
    /// Format identifier: format = 1
    pub fn subst_format(&self) -> u16 {
        let range = self.shape.subst_format_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Offset to Coverage table, from beginning of substitution
    /// subtable
    pub fn coverage_offset(&self) -> Offset16 {
        let range = self.shape.coverage_offset_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
        let data = self.data;
        self.coverage_offset().resolve(data)
    }

    /// Number of Sequence table offsets in the sequenceOffsets array
    pub fn sequence_count(&self) -> u16 {
        let range = self.shape.sequence_count_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Array of offsets to Sequence tables. Offsets are from beginning
    /// of substitution subtable, ordered by Coverage index
    pub fn sequence_offsets(&self) -> &'a [BigEndian<Offset16>] {
        let range = self.shape.sequence_offsets_byte_range();
        self.data.read_array(range).unwrap()
    }

    /// A dynamically resolving wrapper for [`sequence_offsets`][Self::sequence_offsets].
    pub fn sequences(&self) -> ArrayOfOffsets<'a, Sequence<'a>, Offset16> {
        let data = self.data;
        let offsets = self.sequence_offsets();
        ArrayOfOffsets::new(offsets, data, ())
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for MultipleSubstFormat1<'a> {
    fn type_name(&self) -> &str {
        "MultipleSubstFormat1"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("subst_format", self.subst_format())),
            1usize => Some(Field::new(
                "coverage_offset",
                FieldType::offset(self.coverage_offset(), self.coverage()),
            )),
            2usize => Some(Field::new("sequence_count", self.sequence_count())),
            3usize => Some({
                let data = self.data;
                Field::new(
                    "sequence_offsets",
                    FieldType::array_of_offsets(
                        better_type_name::<Sequence>(),
                        self.sequence_offsets(),
                        move |off| {
                            let target = off.get().resolve::<Sequence>(data);
                            FieldType::offset(off.get(), target)
                        },
                    ),
                )
            }),
            _ => None,
        }
    }
}

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

/// Part of [MultipleSubstFormat1]
#[derive(Debug, Clone, Copy)]
#[doc(hidden)]
pub struct SequenceMarker {
    substitute_glyph_ids_byte_len: usize,
}

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

    pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
        let start = self.glyph_count_byte_range().end;
        start..start + self.substitute_glyph_ids_byte_len
    }
}

impl MinByteRange for SequenceMarker {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.substitute_glyph_ids_byte_range().end
    }
}

impl<'a> FontRead<'a> for Sequence<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        let mut cursor = data.cursor();
        let glyph_count: u16 = cursor.read()?;
        let substitute_glyph_ids_byte_len = (glyph_count as usize)
            .checked_mul(GlyphId16::RAW_BYTE_LEN)
            .ok_or(ReadError::OutOfBounds)?;
        cursor.advance_by(substitute_glyph_ids_byte_len);
        cursor.finish(SequenceMarker {
            substitute_glyph_ids_byte_len,
        })
    }
}

/// Part of [MultipleSubstFormat1]
pub type Sequence<'a> = TableRef<'a, SequenceMarker>;

#[allow(clippy::needless_lifetimes)]
impl<'a> Sequence<'a> {
    /// Number of glyph IDs in the substituteGlyphIDs array. This must
    /// always be greater than 0.
    pub fn glyph_count(&self) -> u16 {
        let range = self.shape.glyph_count_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// String of glyph IDs to substitute
    pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
        let range = self.shape.substitute_glyph_ids_byte_range();
        self.data.read_array(range).unwrap()
    }
}

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

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

impl Format<u16> for AlternateSubstFormat1Marker {
    const FORMAT: u16 = 1;
}

/// [Alternate Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#31-alternate-substitution-format-1)
#[derive(Debug, Clone, Copy)]
#[doc(hidden)]
pub struct AlternateSubstFormat1Marker {
    alternate_set_offsets_byte_len: usize,
}

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

    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
        let start = self.subst_format_byte_range().end;
        start..start + Offset16::RAW_BYTE_LEN
    }

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

    pub fn alternate_set_offsets_byte_range(&self) -> Range<usize> {
        let start = self.alternate_set_count_byte_range().end;
        start..start + self.alternate_set_offsets_byte_len
    }
}

impl MinByteRange for AlternateSubstFormat1Marker {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.alternate_set_offsets_byte_range().end
    }
}

impl<'a> FontRead<'a> for AlternateSubstFormat1<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        let mut cursor = data.cursor();
        cursor.advance::<u16>();
        cursor.advance::<Offset16>();
        let alternate_set_count: u16 = cursor.read()?;
        let alternate_set_offsets_byte_len = (alternate_set_count as usize)
            .checked_mul(Offset16::RAW_BYTE_LEN)
            .ok_or(ReadError::OutOfBounds)?;
        cursor.advance_by(alternate_set_offsets_byte_len);
        cursor.finish(AlternateSubstFormat1Marker {
            alternate_set_offsets_byte_len,
        })
    }
}

/// [Alternate Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#31-alternate-substitution-format-1)
pub type AlternateSubstFormat1<'a> = TableRef<'a, AlternateSubstFormat1Marker>;

#[allow(clippy::needless_lifetimes)]
impl<'a> AlternateSubstFormat1<'a> {
    /// Format identifier: format = 1
    pub fn subst_format(&self) -> u16 {
        let range = self.shape.subst_format_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Offset to Coverage table, from beginning of substitution
    /// subtable
    pub fn coverage_offset(&self) -> Offset16 {
        let range = self.shape.coverage_offset_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
        let data = self.data;
        self.coverage_offset().resolve(data)
    }

    /// Number of AlternateSet tables
    pub fn alternate_set_count(&self) -> u16 {
        let range = self.shape.alternate_set_count_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Array of offsets to AlternateSet tables. Offsets are from
    /// beginning of substitution subtable, ordered by Coverage index
    pub fn alternate_set_offsets(&self) -> &'a [BigEndian<Offset16>] {
        let range = self.shape.alternate_set_offsets_byte_range();
        self.data.read_array(range).unwrap()
    }

    /// A dynamically resolving wrapper for [`alternate_set_offsets`][Self::alternate_set_offsets].
    pub fn alternate_sets(&self) -> ArrayOfOffsets<'a, AlternateSet<'a>, Offset16> {
        let data = self.data;
        let offsets = self.alternate_set_offsets();
        ArrayOfOffsets::new(offsets, data, ())
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for AlternateSubstFormat1<'a> {
    fn type_name(&self) -> &str {
        "AlternateSubstFormat1"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("subst_format", self.subst_format())),
            1usize => Some(Field::new(
                "coverage_offset",
                FieldType::offset(self.coverage_offset(), self.coverage()),
            )),
            2usize => Some(Field::new(
                "alternate_set_count",
                self.alternate_set_count(),
            )),
            3usize => Some({
                let data = self.data;
                Field::new(
                    "alternate_set_offsets",
                    FieldType::array_of_offsets(
                        better_type_name::<AlternateSet>(),
                        self.alternate_set_offsets(),
                        move |off| {
                            let target = off.get().resolve::<AlternateSet>(data);
                            FieldType::offset(off.get(), target)
                        },
                    ),
                )
            }),
            _ => None,
        }
    }
}

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

/// Part of [AlternateSubstFormat1]
#[derive(Debug, Clone, Copy)]
#[doc(hidden)]
pub struct AlternateSetMarker {
    alternate_glyph_ids_byte_len: usize,
}

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

    pub fn alternate_glyph_ids_byte_range(&self) -> Range<usize> {
        let start = self.glyph_count_byte_range().end;
        start..start + self.alternate_glyph_ids_byte_len
    }
}

impl MinByteRange for AlternateSetMarker {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.alternate_glyph_ids_byte_range().end
    }
}

impl<'a> FontRead<'a> for AlternateSet<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        let mut cursor = data.cursor();
        let glyph_count: u16 = cursor.read()?;
        let alternate_glyph_ids_byte_len = (glyph_count as usize)
            .checked_mul(GlyphId16::RAW_BYTE_LEN)
            .ok_or(ReadError::OutOfBounds)?;
        cursor.advance_by(alternate_glyph_ids_byte_len);
        cursor.finish(AlternateSetMarker {
            alternate_glyph_ids_byte_len,
        })
    }
}

/// Part of [AlternateSubstFormat1]
pub type AlternateSet<'a> = TableRef<'a, AlternateSetMarker>;

#[allow(clippy::needless_lifetimes)]
impl<'a> AlternateSet<'a> {
    /// Number of glyph IDs in the alternateGlyphIDs array
    pub fn glyph_count(&self) -> u16 {
        let range = self.shape.glyph_count_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Array of alternate glyph IDs, in arbitrary order
    pub fn alternate_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
        let range = self.shape.alternate_glyph_ids_byte_range();
        self.data.read_array(range).unwrap()
    }
}

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

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

impl Format<u16> for LigatureSubstFormat1Marker {
    const FORMAT: u16 = 1;
}

/// [Ligature Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#41-ligature-substitution-format-1)
#[derive(Debug, Clone, Copy)]
#[doc(hidden)]
pub struct LigatureSubstFormat1Marker {
    ligature_set_offsets_byte_len: usize,
}

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

    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
        let start = self.subst_format_byte_range().end;
        start..start + Offset16::RAW_BYTE_LEN
    }

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

    pub fn ligature_set_offsets_byte_range(&self) -> Range<usize> {
        let start = self.ligature_set_count_byte_range().end;
        start..start + self.ligature_set_offsets_byte_len
    }
}

impl MinByteRange for LigatureSubstFormat1Marker {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.ligature_set_offsets_byte_range().end
    }
}

impl<'a> FontRead<'a> for LigatureSubstFormat1<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        let mut cursor = data.cursor();
        cursor.advance::<u16>();
        cursor.advance::<Offset16>();
        let ligature_set_count: u16 = cursor.read()?;
        let ligature_set_offsets_byte_len = (ligature_set_count as usize)
            .checked_mul(Offset16::RAW_BYTE_LEN)
            .ok_or(ReadError::OutOfBounds)?;
        cursor.advance_by(ligature_set_offsets_byte_len);
        cursor.finish(LigatureSubstFormat1Marker {
            ligature_set_offsets_byte_len,
        })
    }
}

/// [Ligature Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#41-ligature-substitution-format-1)
pub type LigatureSubstFormat1<'a> = TableRef<'a, LigatureSubstFormat1Marker>;

#[allow(clippy::needless_lifetimes)]
impl<'a> LigatureSubstFormat1<'a> {
    /// Format identifier: format = 1
    pub fn subst_format(&self) -> u16 {
        let range = self.shape.subst_format_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Offset to Coverage table, from beginning of substitution
    /// subtable
    pub fn coverage_offset(&self) -> Offset16 {
        let range = self.shape.coverage_offset_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
        let data = self.data;
        self.coverage_offset().resolve(data)
    }

    /// Number of LigatureSet tables
    pub fn ligature_set_count(&self) -> u16 {
        let range = self.shape.ligature_set_count_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Array of offsets to LigatureSet tables. Offsets are from
    /// beginning of substitution subtable, ordered by Coverage index
    pub fn ligature_set_offsets(&self) -> &'a [BigEndian<Offset16>] {
        let range = self.shape.ligature_set_offsets_byte_range();
        self.data.read_array(range).unwrap()
    }

    /// A dynamically resolving wrapper for [`ligature_set_offsets`][Self::ligature_set_offsets].
    pub fn ligature_sets(&self) -> ArrayOfOffsets<'a, LigatureSet<'a>, Offset16> {
        let data = self.data;
        let offsets = self.ligature_set_offsets();
        ArrayOfOffsets::new(offsets, data, ())
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for LigatureSubstFormat1<'a> {
    fn type_name(&self) -> &str {
        "LigatureSubstFormat1"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("subst_format", self.subst_format())),
            1usize => Some(Field::new(
                "coverage_offset",
                FieldType::offset(self.coverage_offset(), self.coverage()),
            )),
            2usize => Some(Field::new("ligature_set_count", self.ligature_set_count())),
            3usize => Some({
                let data = self.data;
                Field::new(
                    "ligature_set_offsets",
                    FieldType::array_of_offsets(
                        better_type_name::<LigatureSet>(),
                        self.ligature_set_offsets(),
                        move |off| {
                            let target = off.get().resolve::<LigatureSet>(data);
                            FieldType::offset(off.get(), target)
                        },
                    ),
                )
            }),
            _ => None,
        }
    }
}

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

/// Part of [LigatureSubstFormat1]
#[derive(Debug, Clone, Copy)]
#[doc(hidden)]
pub struct LigatureSetMarker {
    ligature_offsets_byte_len: usize,
}

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

    pub fn ligature_offsets_byte_range(&self) -> Range<usize> {
        let start = self.ligature_count_byte_range().end;
        start..start + self.ligature_offsets_byte_len
    }
}

impl MinByteRange for LigatureSetMarker {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.ligature_offsets_byte_range().end
    }
}

impl<'a> FontRead<'a> for LigatureSet<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        let mut cursor = data.cursor();
        let ligature_count: u16 = cursor.read()?;
        let ligature_offsets_byte_len = (ligature_count as usize)
            .checked_mul(Offset16::RAW_BYTE_LEN)
            .ok_or(ReadError::OutOfBounds)?;
        cursor.advance_by(ligature_offsets_byte_len);
        cursor.finish(LigatureSetMarker {
            ligature_offsets_byte_len,
        })
    }
}

/// Part of [LigatureSubstFormat1]
pub type LigatureSet<'a> = TableRef<'a, LigatureSetMarker>;

#[allow(clippy::needless_lifetimes)]
impl<'a> LigatureSet<'a> {
    /// Number of Ligature tables
    pub fn ligature_count(&self) -> u16 {
        let range = self.shape.ligature_count_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Array of offsets to Ligature tables. Offsets are from beginning
    /// of LigatureSet table, ordered by preference.
    pub fn ligature_offsets(&self) -> &'a [BigEndian<Offset16>] {
        let range = self.shape.ligature_offsets_byte_range();
        self.data.read_array(range).unwrap()
    }

    /// A dynamically resolving wrapper for [`ligature_offsets`][Self::ligature_offsets].
    pub fn ligatures(&self) -> ArrayOfOffsets<'a, Ligature<'a>, Offset16> {
        let data = self.data;
        let offsets = self.ligature_offsets();
        ArrayOfOffsets::new(offsets, data, ())
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for LigatureSet<'a> {
    fn type_name(&self) -> &str {
        "LigatureSet"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("ligature_count", self.ligature_count())),
            1usize => Some({
                let data = self.data;
                Field::new(
                    "ligature_offsets",
                    FieldType::array_of_offsets(
                        better_type_name::<Ligature>(),
                        self.ligature_offsets(),
                        move |off| {
                            let target = off.get().resolve::<Ligature>(data);
                            FieldType::offset(off.get(), target)
                        },
                    ),
                )
            }),
            _ => None,
        }
    }
}

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

/// Part of [LigatureSubstFormat1]
#[derive(Debug, Clone, Copy)]
#[doc(hidden)]
pub struct LigatureMarker {
    component_glyph_ids_byte_len: usize,
}

impl LigatureMarker {
    pub fn ligature_glyph_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + GlyphId16::RAW_BYTE_LEN
    }

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

    pub fn component_glyph_ids_byte_range(&self) -> Range<usize> {
        let start = self.component_count_byte_range().end;
        start..start + self.component_glyph_ids_byte_len
    }
}

impl MinByteRange for LigatureMarker {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.component_glyph_ids_byte_range().end
    }
}

impl<'a> FontRead<'a> for Ligature<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        let mut cursor = data.cursor();
        cursor.advance::<GlyphId16>();
        let component_count: u16 = cursor.read()?;
        let component_glyph_ids_byte_len = (transforms::subtract(component_count, 1_usize))
            .checked_mul(GlyphId16::RAW_BYTE_LEN)
            .ok_or(ReadError::OutOfBounds)?;
        cursor.advance_by(component_glyph_ids_byte_len);
        cursor.finish(LigatureMarker {
            component_glyph_ids_byte_len,
        })
    }
}

/// Part of [LigatureSubstFormat1]
pub type Ligature<'a> = TableRef<'a, LigatureMarker>;

#[allow(clippy::needless_lifetimes)]
impl<'a> Ligature<'a> {
    /// glyph ID of ligature to substitute
    pub fn ligature_glyph(&self) -> GlyphId16 {
        let range = self.shape.ligature_glyph_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Number of components in the ligature
    pub fn component_count(&self) -> u16 {
        let range = self.shape.component_count_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Array of component glyph IDs — start with the second
    /// component, ordered in writing direction
    pub fn component_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
        let range = self.shape.component_glyph_ids_byte_range();
        self.data.read_array(range).unwrap()
    }
}

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

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

impl Format<u16> for ExtensionSubstFormat1Marker {
    const FORMAT: u16 = 1;
}

/// [Extension Substitution Subtable Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#71-extension-substitution-subtable-format-1)
#[derive(Debug)]
#[doc(hidden)]
pub struct ExtensionSubstFormat1Marker<T = ()> {
    offset_type: std::marker::PhantomData<*const T>,
}

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

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

    pub fn extension_offset_byte_range(&self) -> Range<usize> {
        let start = self.extension_lookup_type_byte_range().end;
        start..start + Offset32::RAW_BYTE_LEN
    }
}

impl MinByteRange for ExtensionSubstFormat1Marker {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.extension_offset_byte_range().end
    }
}

impl<T> Clone for ExtensionSubstFormat1Marker<T> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<T> Copy for ExtensionSubstFormat1Marker<T> {}

impl<'a, T> FontRead<'a> for ExtensionSubstFormat1<'a, T> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        let mut cursor = data.cursor();
        cursor.advance::<u16>();
        cursor.advance::<u16>();
        cursor.advance::<Offset32>();
        cursor.finish(ExtensionSubstFormat1Marker {
            offset_type: std::marker::PhantomData,
        })
    }
}

impl<'a> ExtensionSubstFormat1<'a, ()> {
    #[allow(dead_code)]
    pub(crate) fn into_concrete<T>(self) -> ExtensionSubstFormat1<'a, T> {
        let TableRef { data, .. } = self;
        TableRef {
            shape: ExtensionSubstFormat1Marker {
                offset_type: std::marker::PhantomData,
            },
            data,
        }
    }
}

impl<'a, T> ExtensionSubstFormat1<'a, T> {
    #[allow(dead_code)]
    /// Replace the specific generic type on this implementation with `()`
    pub(crate) fn of_unit_type(&self) -> ExtensionSubstFormat1<'a, ()> {
        let TableRef { data, .. } = self;
        TableRef {
            shape: ExtensionSubstFormat1Marker {
                offset_type: std::marker::PhantomData,
            },
            data: *data,
        }
    }
}

/// [Extension Substitution Subtable Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#71-extension-substitution-subtable-format-1)
pub type ExtensionSubstFormat1<'a, T> = TableRef<'a, ExtensionSubstFormat1Marker<T>>;

#[allow(clippy::needless_lifetimes)]
impl<'a, T> ExtensionSubstFormat1<'a, T> {
    /// Format identifier. Set to 1.
    pub fn subst_format(&self) -> u16 {
        let range = self.shape.subst_format_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Lookup type of subtable referenced by extensionOffset (that is,
    /// the extension subtable).
    pub fn extension_lookup_type(&self) -> u16 {
        let range = self.shape.extension_lookup_type_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Offset to the extension subtable, of lookup type
    /// extensionLookupType, relative to the start of the
    /// ExtensionSubstFormat1 subtable.
    pub fn extension_offset(&self) -> Offset32 {
        let range = self.shape.extension_offset_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Attempt to resolve [`extension_offset`][Self::extension_offset].
    pub fn extension(&self) -> Result<T, ReadError>
    where
        T: FontRead<'a>,
    {
        let data = self.data;
        self.extension_offset().resolve(data)
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> SomeTable<'a> for ExtensionSubstFormat1<'a, T> {
    fn type_name(&self) -> &str {
        "ExtensionSubstFormat1"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("subst_format", self.subst_format())),
            1usize => Some(Field::new(
                "extension_lookup_type",
                self.extension_lookup_type(),
            )),
            2usize => Some(Field::new(
                "extension_offset",
                FieldType::offset(self.extension_offset(), self.extension()),
            )),
            _ => None,
        }
    }
}

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

/// A [GSUB Extension Substitution](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#ES) subtable
pub enum ExtensionSubtable<'a> {
    Single(ExtensionSubstFormat1<'a, SingleSubst<'a>>),
    Multiple(ExtensionSubstFormat1<'a, MultipleSubstFormat1<'a>>),
    Alternate(ExtensionSubstFormat1<'a, AlternateSubstFormat1<'a>>),
    Ligature(ExtensionSubstFormat1<'a, LigatureSubstFormat1<'a>>),
    Contextual(ExtensionSubstFormat1<'a, SubstitutionSequenceContext<'a>>),
    ChainContextual(ExtensionSubstFormat1<'a, SubstitutionChainContext<'a>>),
    Reverse(ExtensionSubstFormat1<'a, ReverseChainSingleSubstFormat1<'a>>),
}

impl<'a> FontRead<'a> for ExtensionSubtable<'a> {
    fn read(bytes: FontData<'a>) -> Result<Self, ReadError> {
        let untyped = ExtensionSubstFormat1::read(bytes)?;
        match untyped.extension_lookup_type() {
            1 => Ok(ExtensionSubtable::Single(untyped.into_concrete())),
            2 => Ok(ExtensionSubtable::Multiple(untyped.into_concrete())),
            3 => Ok(ExtensionSubtable::Alternate(untyped.into_concrete())),
            4 => Ok(ExtensionSubtable::Ligature(untyped.into_concrete())),
            5 => Ok(ExtensionSubtable::Contextual(untyped.into_concrete())),
            6 => Ok(ExtensionSubtable::ChainContextual(untyped.into_concrete())),
            8 => Ok(ExtensionSubtable::Reverse(untyped.into_concrete())),
            other => Err(ReadError::InvalidFormat(other.into())),
        }
    }
}

impl<'a> ExtensionSubtable<'a> {
    #[allow(dead_code)]
    /// Return the inner table, removing the specific generics.
    ///
    /// This lets us return a single concrete type we can call methods on.
    pub(crate) fn of_unit_type(&self) -> ExtensionSubstFormat1<'a, ()> {
        match self {
            ExtensionSubtable::Single(inner) => inner.of_unit_type(),
            ExtensionSubtable::Multiple(inner) => inner.of_unit_type(),
            ExtensionSubtable::Alternate(inner) => inner.of_unit_type(),
            ExtensionSubtable::Ligature(inner) => inner.of_unit_type(),
            ExtensionSubtable::Contextual(inner) => inner.of_unit_type(),
            ExtensionSubtable::ChainContextual(inner) => inner.of_unit_type(),
            ExtensionSubtable::Reverse(inner) => inner.of_unit_type(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> ExtensionSubtable<'a> {
    fn dyn_inner(&self) -> &(dyn SomeTable<'a> + 'a) {
        match self {
            ExtensionSubtable::Single(table) => table,
            ExtensionSubtable::Multiple(table) => table,
            ExtensionSubtable::Alternate(table) => table,
            ExtensionSubtable::Ligature(table) => table,
            ExtensionSubtable::Contextual(table) => table,
            ExtensionSubtable::ChainContextual(table) => table,
            ExtensionSubtable::Reverse(table) => table,
        }
    }
}

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

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

impl Format<u16> for ReverseChainSingleSubstFormat1Marker {
    const FORMAT: u16 = 1;
}

/// [Reverse Chaining Contextual Single Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#81-reverse-chaining-contextual-single-substitution-format-1-coverage-based-glyph-contexts)
#[derive(Debug, Clone, Copy)]
#[doc(hidden)]
pub struct ReverseChainSingleSubstFormat1Marker {
    backtrack_coverage_offsets_byte_len: usize,
    lookahead_coverage_offsets_byte_len: usize,
    substitute_glyph_ids_byte_len: usize,
}

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

    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
        let start = self.subst_format_byte_range().end;
        start..start + Offset16::RAW_BYTE_LEN
    }

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

    pub fn backtrack_coverage_offsets_byte_range(&self) -> Range<usize> {
        let start = self.backtrack_glyph_count_byte_range().end;
        start..start + self.backtrack_coverage_offsets_byte_len
    }

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

    pub fn lookahead_coverage_offsets_byte_range(&self) -> Range<usize> {
        let start = self.lookahead_glyph_count_byte_range().end;
        start..start + self.lookahead_coverage_offsets_byte_len
    }

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

    pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
        let start = self.glyph_count_byte_range().end;
        start..start + self.substitute_glyph_ids_byte_len
    }
}

impl MinByteRange for ReverseChainSingleSubstFormat1Marker {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.substitute_glyph_ids_byte_range().end
    }
}

impl<'a> FontRead<'a> for ReverseChainSingleSubstFormat1<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        let mut cursor = data.cursor();
        cursor.advance::<u16>();
        cursor.advance::<Offset16>();
        let backtrack_glyph_count: u16 = cursor.read()?;
        let backtrack_coverage_offsets_byte_len = (backtrack_glyph_count as usize)
            .checked_mul(Offset16::RAW_BYTE_LEN)
            .ok_or(ReadError::OutOfBounds)?;
        cursor.advance_by(backtrack_coverage_offsets_byte_len);
        let lookahead_glyph_count: u16 = cursor.read()?;
        let lookahead_coverage_offsets_byte_len = (lookahead_glyph_count as usize)
            .checked_mul(Offset16::RAW_BYTE_LEN)
            .ok_or(ReadError::OutOfBounds)?;
        cursor.advance_by(lookahead_coverage_offsets_byte_len);
        let glyph_count: u16 = cursor.read()?;
        let substitute_glyph_ids_byte_len = (glyph_count as usize)
            .checked_mul(GlyphId16::RAW_BYTE_LEN)
            .ok_or(ReadError::OutOfBounds)?;
        cursor.advance_by(substitute_glyph_ids_byte_len);
        cursor.finish(ReverseChainSingleSubstFormat1Marker {
            backtrack_coverage_offsets_byte_len,
            lookahead_coverage_offsets_byte_len,
            substitute_glyph_ids_byte_len,
        })
    }
}

/// [Reverse Chaining Contextual Single Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#81-reverse-chaining-contextual-single-substitution-format-1-coverage-based-glyph-contexts)
pub type ReverseChainSingleSubstFormat1<'a> = TableRef<'a, ReverseChainSingleSubstFormat1Marker>;

#[allow(clippy::needless_lifetimes)]
impl<'a> ReverseChainSingleSubstFormat1<'a> {
    /// Format identifier: format = 1
    pub fn subst_format(&self) -> u16 {
        let range = self.shape.subst_format_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Offset to Coverage table, from beginning of substitution
    /// subtable.
    pub fn coverage_offset(&self) -> Offset16 {
        let range = self.shape.coverage_offset_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
        let data = self.data;
        self.coverage_offset().resolve(data)
    }

    /// Number of glyphs in the backtrack sequence.
    pub fn backtrack_glyph_count(&self) -> u16 {
        let range = self.shape.backtrack_glyph_count_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Array of offsets to coverage tables in backtrack sequence, in
    /// glyph sequence order.
    pub fn backtrack_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
        let range = self.shape.backtrack_coverage_offsets_byte_range();
        self.data.read_array(range).unwrap()
    }

    /// A dynamically resolving wrapper for [`backtrack_coverage_offsets`][Self::backtrack_coverage_offsets].
    pub fn backtrack_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
        let data = self.data;
        let offsets = self.backtrack_coverage_offsets();
        ArrayOfOffsets::new(offsets, data, ())
    }

    /// Number of glyphs in lookahead sequence.
    pub fn lookahead_glyph_count(&self) -> u16 {
        let range = self.shape.lookahead_glyph_count_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Array of offsets to coverage tables in lookahead sequence, in
    /// glyph sequence order.
    pub fn lookahead_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
        let range = self.shape.lookahead_coverage_offsets_byte_range();
        self.data.read_array(range).unwrap()
    }

    /// A dynamically resolving wrapper for [`lookahead_coverage_offsets`][Self::lookahead_coverage_offsets].
    pub fn lookahead_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
        let data = self.data;
        let offsets = self.lookahead_coverage_offsets();
        ArrayOfOffsets::new(offsets, data, ())
    }

    /// Number of glyph IDs in the substituteGlyphIDs array.
    pub fn glyph_count(&self) -> u16 {
        let range = self.shape.glyph_count_byte_range();
        self.data.read_at(range.start).unwrap()
    }

    /// Array of substitute glyph IDs — ordered by Coverage index.
    pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
        let range = self.shape.substitute_glyph_ids_byte_range();
        self.data.read_array(range).unwrap()
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for ReverseChainSingleSubstFormat1<'a> {
    fn type_name(&self) -> &str {
        "ReverseChainSingleSubstFormat1"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("subst_format", self.subst_format())),
            1usize => Some(Field::new(
                "coverage_offset",
                FieldType::offset(self.coverage_offset(), self.coverage()),
            )),
            2usize => Some(Field::new(
                "backtrack_glyph_count",
                self.backtrack_glyph_count(),
            )),
            3usize => Some({
                let data = self.data;
                Field::new(
                    "backtrack_coverage_offsets",
                    FieldType::array_of_offsets(
                        better_type_name::<CoverageTable>(),
                        self.backtrack_coverage_offsets(),
                        move |off| {
                            let target = off.get().resolve::<CoverageTable>(data);
                            FieldType::offset(off.get(), target)
                        },
                    ),
                )
            }),
            4usize => Some(Field::new(
                "lookahead_glyph_count",
                self.lookahead_glyph_count(),
            )),
            5usize => Some({
                let data = self.data;
                Field::new(
                    "lookahead_coverage_offsets",
                    FieldType::array_of_offsets(
                        better_type_name::<CoverageTable>(),
                        self.lookahead_coverage_offsets(),
                        move |off| {
                            let target = off.get().resolve::<CoverageTable>(data);
                            FieldType::offset(off.get(), target)
                        },
                    ),
                )
            }),
            6usize => Some(Field::new("glyph_count", self.glyph_count())),
            7usize => Some(Field::new(
                "substitute_glyph_ids",
                self.substitute_glyph_ids(),
            )),
            _ => None,
        }
    }
}

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