Documentation
use super::*;
use crate::{TableIndex, TypeReader};

#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Debug)]
pub struct TypeDef(pub Row);

impl TypeDef {
    pub fn flags(self, reader: &TypeReader) -> TypeFlags {
        TypeFlags(reader.u32(self.0, 0))
    }

    pub fn name(self, reader: &TypeReader) -> (&str, &str) {
        (reader.str(self.0, 2), reader.str(self.0, 1))
    }

    pub fn extends(self, reader: &TypeReader) -> TypeDefOrRef {
        reader.decode(self.0, 3)
    }

    pub fn fields(self, reader: &TypeReader) -> impl Iterator<Item = Field> {
        reader.list(self.0, TableIndex::Field, 4).map(Field)
    }

    pub fn methods(self, reader: &TypeReader) -> impl Iterator<Item = MethodDef> {
        reader.list(self.0, TableIndex::MethodDef, 5).map(MethodDef)
    }

    pub fn generics(self, reader: &TypeReader) -> impl Iterator<Item = GenericParam> {
        reader
            .equal_range(
                self.0.file_index,
                TableIndex::GenericParam,
                2,
                TypeOrMethodDef::TypeDef(self).encode(),
            )
            .map(GenericParam)
    }

    pub fn interfaces(self, reader: &TypeReader) -> impl Iterator<Item = InterfaceImpl> {
        reader
            .equal_range(
                self.0.file_index,
                TableIndex::InterfaceImpl,
                0,
                self.0.index + 1,
            )
            .map(InterfaceImpl)
    }

    pub fn attributes(self, reader: &TypeReader) -> impl Iterator<Item = Attribute> {
        reader
            .equal_range(
                self.0.file_index,
                TableIndex::CustomAttribute,
                0,
                HasAttribute::TypeDef(self).encode(),
            )
            .map(Attribute)
    }

    pub fn has_attribute(self, reader: &TypeReader, name: (&str, &str)) -> bool {
        self.attributes(reader)
            .any(|attribute| attribute.name(reader) == name)
    }

    pub fn attribute(self, reader: &TypeReader, name: (&str, &str)) -> Attribute {
        self.attributes(reader)
            .find(|attribute| attribute.name(reader) == name)
            .unwrap()
    }

    pub fn is_winrt(self, reader: &TypeReader) -> bool {
        let flags = self.flags(reader);

        if !flags.windows_runtime() {
            false
        } else if flags.interface() {
            true
        } else {
            match self.extends(reader).name(reader) {
                ("System", "ValueType") => !self.has_attribute(
                    reader,
                    ("Windows.Foundation.Metadata", "ApiContractAttribute"),
                ),
                ("System", "Attribute") => false,
                _ => true,
            }
        }
    }

    pub fn category(self, reader: &TypeReader) -> TypeCategory {
        if self.flags(reader).interface() {
            TypeCategory::Interface
        } else {
            match self.extends(reader).name(reader) {
                ("System", "Enum") => TypeCategory::Enum,
                ("System", "MulticastDelegate") => TypeCategory::Delegate,
                ("System", "ValueType") => TypeCategory::Struct,
                _ => TypeCategory::Class,
            }
        }
    }

    pub fn underlying_type(self, reader: &TypeReader) -> ElementType {
        for field in self.fields(reader) {
            for constant in field.constants(reader) {
                return constant.value_type(reader);
            }
        }

        panic!("TypeDef::underlying_type");
    }
}