//! The map item: <https://source.android.com/docs/core/runtime/dex-format#map-list>

use crate as androscalpel_serializer;
use crate::core::{ReadSeek, Result, Serializable};
use std::io::Write;

/// <https://source.android.com/docs/core/runtime/dex-format#map-list>
/// alignment: 4 bytes
#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub struct MapList {
    // pub size: u32,
    pub list: Vec<MapItem>,
}

/// The location and size of an item: <https://source.android.com/docs/core/runtime/dex-format#map-item>
#[derive(Serializable, Clone, PartialEq, Eq, Debug)]
pub struct MapItem {
    pub type_: MapItemType,
    pub unused: u16,
    pub size: u32,
    pub offset: u32,
}

/// The type of the items refered by a [`MapItem`]: <https://source.android.com/docs/core/runtime/dex-format#type-codes>
#[derive(Serializable, Clone, Copy, PartialEq, Eq, Debug, Hash)]
#[prefix_type(u16)]
pub enum MapItemType {
    #[prefix(0x0000)]
    HeaderItem,
    #[prefix(0x0001)]
    StringIdItem,
    #[prefix(0x0002)]
    TypeIdItem,
    #[prefix(0x0003)]
    ProtoIdItem,
    #[prefix(0x0004)]
    FieldIdItem,
    #[prefix(0x0005)]
    MethodIdItem,
    #[prefix(0x0006)]
    ClassDefItem,
    #[prefix(0x0007)]
    CallSiteIdItem,
    #[prefix(0x0008)]
    MethodHandleItem,
    #[prefix(0x1000)]
    MapList,
    #[prefix(0x1001)]
    TypeList,
    #[prefix(0x1002)]
    AnnotationSetRefList,
    #[prefix(0x1003)]
    AnnotationSetItem,
    #[prefix(0x2000)]
    ClassDataItem,
    #[prefix(0x2001)]
    CodeItem,
    #[prefix(0x2002)]
    StringDataItem,
    #[prefix(0x2003)]
    DebugInfoItem,
    #[prefix(0x2004)]
    AnnotationItem,
    #[prefix(0x2005)]
    EncodedArrayItem,
    #[prefix(0x2006)]
    AnnotationsDirectoryItem,
    #[prefix(0xF000)]
    HiddenapiClassDataItem,
    #[default_variant]
    UnkownType(u16),
}

impl MapList {
    /// The size field of a MapList.
    pub fn size_field(&self) -> u32 {
        self.list.len() as u32
    }
}

impl Serializable for MapList {
    fn serialize(&self, output: &mut dyn Write) -> Result<()> {
        self.size_field().serialize(output)?;
        for map_item in &self.list {
            map_item.serialize(output)?;
        }
        Ok(())
    }

    fn deserialize(input: &mut dyn ReadSeek) -> Result<Self> {
        let size = u32::deserialize(input)?;
        let mut list = vec![];
        for _ in 0..size {
            list.push(MapItem::deserialize(input)?);
        }
        Ok(Self { list })
    }

    fn size(&self) -> usize {
        self.size_field().size() + self.list.iter().map(|item| item.size()).sum::<usize>()
    }
}

// TODO: add tests
