// 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 Discriminant for MyLookup<'_, ()> {
    fn read_discriminant(data: FontData<'_>) -> Result<u16, ReadError> {
        data.read_at(0)
    }
}

impl<'a, T> MinByteRange<'a> for MyLookup<'a, T> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.subtable_offsets_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, T> FontRead<'a> for MyLookup<'a, T> {
    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,
            offset_type: std::marker::PhantomData,
        })
    }
}

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

/// A generic table parameterized by the type of its subtable offsets.
#[derive(Clone)]
pub struct MyLookup<'a, T = ()> {
    data: FontData<'a>,
    offset_type: std::marker::PhantomData<*const T>,
}

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

    /// Determines the concrete type of T
    pub fn lookup_type(&self) -> u16 {
        let range = self.lookup_type_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

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

    /// Offsets to subtables, from beginning of this table
    pub fn subtable_offsets(&self) -> &'a [BigEndian<Offset16>] {
        let range = self.subtable_offsets_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

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

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

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

    pub fn subtable_offsets_byte_range(&self) -> Range<usize> {
        let sub_table_count = self.sub_table_count();
        let start = self.sub_table_count_byte_range().end;
        let end =
            start + (transforms::to_usize(sub_table_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
        start..end
    }
}

const _: () = assert!(FontData::default_data_long_enough(MyLookup::<()>::MIN_SIZE));

impl<T> Default for MyLookup<'_, T> {
    fn default() -> Self {
        Self {
            data: FontData::default_table_data(),
            offset_type: std::marker::PhantomData,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> SomeTable<'a> for MyLookup<'a, T> {
    fn type_name(&self) -> &str {
        "MyLookup"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("lookup_type", self.lookup_type())),
            1usize => Some(Field::new("sub_table_count", self.sub_table_count())),
            2usize => Some(Field::new(
                "subtable_offsets",
                FieldType::from(self.subtables()),
            )),
            _ => None,
        }
    }
}

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

/// A concrete subtable with a format field (format group)
#[derive(Clone)]
pub enum MySubtable<'a> {
    Format1(MySubtableFormat1<'a>),
    Format2(MySubtableFormat2<'a>),
}

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

impl<'a> MySubtable<'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(),
        }
    }

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

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

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

#[cfg(feature = "experimental_traverse")]
impl<'a> MySubtable<'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 MySubtable<'_> {
    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 MySubtable<'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 MySubtableFormat1<'_> {
    const FORMAT: u16 = 1;
}

impl<'a> MinByteRange<'a> for MySubtableFormat1<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.value_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 MySubtableFormat1<'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 })
    }
}

#[derive(Clone)]
pub struct MySubtableFormat1<'a> {
    data: FontData<'a>,
}

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

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

    pub fn value(&self) -> u16 {
        let range = self.value_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

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

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

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

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

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

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

impl Format<u16> for MySubtableFormat2<'_> {
    const FORMAT: u16 = 2;
}

impl<'a> MinByteRange<'a> for MySubtableFormat2<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.values_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 MySubtableFormat2<'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 })
    }
}

#[derive(Clone)]
pub struct MySubtableFormat2<'a> {
    data: FontData<'a>,
}

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

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

    pub fn count(&self) -> u16 {
        let range = self.count_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    pub fn values(&self) -> &'a [BigEndian<u16>] {
        let range = self.values_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

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

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

    pub fn values_byte_range(&self) -> Range<usize> {
        let count = self.count();
        let start = self.count_byte_range().end;
        let end = start + (transforms::to_usize(count)).saturating_mul(u16::RAW_BYTE_LEN);
        start..end
    }
}

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

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

/// The group enum dispatching on lookup_type.
pub enum MyLookupGroup<'a> {
    TypeOne(MyLookup<'a, MySubtable<'a>>),
    TypeTwo(MyLookup<'a, MySubtableFormat1<'a>>),
}

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

impl<'a> FontRead<'a> for MyLookupGroup<'a> {
    fn read(bytes: FontData<'a>) -> Result<Self, ReadError> {
        let discriminant = MyLookup::read_discriminant(bytes)?;
        match discriminant {
            1 => Ok(MyLookupGroup::TypeOne(FontRead::read(bytes)?)),
            2 => Ok(MyLookupGroup::TypeTwo(FontRead::read(bytes)?)),
            other => Err(ReadError::InvalidFormat(other.into())),
        }
    }
}

impl<'a> MyLookupGroup<'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) -> MyLookup<'a, ()> {
        match self {
            MyLookupGroup::TypeOne(inner) => inner.of_unit_type(),
            MyLookupGroup::TypeTwo(inner) => inner.of_unit_type(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> MyLookupGroup<'a> {
    fn dyn_inner(&self) -> &(dyn SomeTable<'a> + 'a) {
        match self {
            MyLookupGroup::TypeOne(table) => table,
            MyLookupGroup::TypeTwo(table) => table,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for MyLookupGroup<'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 MyLookupGroup<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.dyn_inner().fmt(f)
    }
}

impl<'a> MinByteRange<'a> for ContainsLookupGroup<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.lookup_offset_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 ContainsLookupGroup<'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 })
    }
}

#[derive(Clone)]
pub struct ContainsLookupGroup<'a> {
    data: FontData<'a>,
}

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

    pub fn version(&self) -> u16 {
        let range = self.version_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    pub fn lookup_offset(&self) -> Offset16 {
        let range = self.lookup_offset_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

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

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

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

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

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

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

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