//! The structure that generate a .dex from classes.
use std::collections::{HashMap, HashSet, VecDeque};

use crate::dex_fragment::{DexFragment, FragSection, FragSectionManager};
use crate::{Class, DexString, IdField, IdMethod, IdMethodType, IdType, Result};
use androscalpel_serializer::{MapItem, MapItemType, MapList, Serializable, TypeItem, TypeList};
use anyhow::{anyhow, bail};

#[derive(Debug, Clone)]
pub struct DexWriter<'a> {
    classes: VecDeque<&'a Class>,
}

impl<'a> Default for DexWriter<'a> {
    fn default() -> Self {
        Self {
            classes: VecDeque::new(),
        }
    }
}

impl<'a> DexWriter<'a> {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn empty(&self) -> bool {
        self.classes.is_empty()
    }

    pub fn add_class(&mut self, class: &'a Class) {
        self.classes.push_back(class)
    }

    /// Take as many fragments as possible and convert them to a dex file.
    pub fn generate_next_dex_file(&mut self) -> Result<Vec<u8>> {
        let mut fragments_in_file = vec![];
        let mut string_set: HashSet<DexString> = HashSet::new();
        let mut type_set: HashSet<IdType> = HashSet::new();
        let mut proto_set: HashSet<IdMethodType> = HashSet::new();
        let mut field_set: HashSet<IdField> = HashSet::new();
        let mut method_set: HashSet<IdMethod> = HashSet::new();
        let mut type_list_set: HashSet<Vec<IdType>> = HashSet::new();
        let fragments: VecDeque<(&'a Class, DexFragment)> = self
            .classes
            .into_iter()
            .map(|class| match DexFragment::new(class) {
                Ok(frag) => Ok((class, frag)),
                Err(err) => Err(err),
            })
            .collect::<Result<_>>()?;
        loop {
            let (class, new_fragment) = if let Some(new_fragment) = fragments.pop_front() {
                new_fragment
            } else {
                break;
            };
            if type_set.len() + new_fragment.type_ids().len() > u16::MAX as usize
                && type_set.len()
                    + new_fragment
                        .type_ids()
                        .iter()
                        .filter(|ty| type_set.contains(*ty))
                        .count()
                    > u16::MAX as usize
            {
                fragments.push_front((class, new_fragment));
                break;
            }
            if proto_set.len() + new_fragment.proto_ids().len() > u16::MAX as usize
                && proto_set.len()
                    + new_fragment
                        .proto_ids()
                        .iter()
                        .filter(|proto| proto_set.contains(*proto))
                        .count()
                    > u16::MAX as usize
            {
                fragments.push_front((class, new_fragment));
                break;
            }
            if field_set.len() + new_fragment.field_ids().len() > u16::MAX as usize
                && field_set.len()
                    + new_fragment
                        .field_ids()
                        .iter()
                        .filter(|field| field_set.contains(*field))
                        .count()
                    > u16::MAX as usize
            {
                fragments.push_front((class, new_fragment));
                break;
            }
            if method_set.len() + new_fragment.method_ids().len() > u16::MAX as usize
                && method_set.len()
                    + new_fragment
                        .method_ids()
                        .iter()
                        .filter(|meth| method_set.contains(*meth))
                        .count()
                    > u16::MAX as usize
            {
                fragments.push_front((class, new_fragment));
                break;
            }
            string_set.extend(new_fragment.strings().iter().cloned());
            type_set.extend(new_fragment.type_ids().iter().cloned());
            proto_set.extend(new_fragment.proto_ids().iter().cloned());
            field_set.extend(new_fragment.field_ids().iter().cloned());
            method_set.extend(new_fragment.method_ids().iter().cloned());
            type_list_set.insert(new_fragment.interfaces().to_vec());
            let (nb_call_site, nb_method_handle) =
                new_fragment.get_nb_call_site_and_method_handle(&class);
            fragments_in_file.push((class, new_fragment, nb_call_site, nb_method_handle));
        }
        type_list_set.extend(proto_set.iter().map(|proto| proto.parameters.clone()));
        let mut strings: Vec<DexString> = string_set.iter().cloned().collect();
        strings.sort();
        let mut type_ids: Vec<IdType> = type_set.iter().cloned().collect();
        type_ids.sort();
        let mut proto_ids: Vec<IdMethodType> = proto_set.iter().cloned().collect();
        proto_ids.sort();
        let mut field_ids: Vec<IdField> = field_set.iter().cloned().collect();
        field_ids.sort();
        let mut method_ids: Vec<IdMethod> = method_set.iter().cloned().collect();
        method_ids.sort();
        let mut type_lists: Vec<Vec<IdType>> = type_list_set.iter().cloned().collect();
        let index = DexIndex::new(&strings, &type_ids, &proto_ids, &field_ids, &method_ids);

        // TODO: Sort fragments_in_file in topological order
        // fragments_in_file.sort() magic stuff
        let mut nb_call_site_total = 0;
        let mut nb_method_handle_total = 0;
        for (_, _, nb_call_site, nb_method_handle) in &mut fragments_in_file {
            let nb_call_site_before_fragment = nb_call_site_total;
            let nb_method_handle_before_fragment = nb_method_handle_total;
            nb_call_site_total += *nb_call_site;
            nb_method_handle_total += *nb_method_handle;
            *nb_call_site = nb_call_site_before_fragment;
            *nb_method_handle = nb_method_handle_before_fragment;
        }

        // Link descriptor ids (and generate code items)
        let fragments_in_file: Vec<DexFragment> = fragments_in_file
            .into_iter()
            .map(
                |(
                    class,
                    fragment,
                    nb_call_site_before_fragment,
                    nb_method_handle_before_fragment,
                )| {
                    match fragment.link_global_ids(
                        class,
                        &index,
                        nb_call_site_before_fragment,
                        nb_method_handle_before_fragment,
                    ) {
                        Err(err) => Err(err),
                        Ok(()) => Ok(fragment),
                    }
                },
            )
            .collect::<Result<_>>()?;

        let mut type_lists = HashMap::<Vec<IdType>, u32>::new();
        // collect section offsets
        // The type are quite uggly but I'm behind on time
        let mut fragment_section_manager = FragSectionManager::default();
        // DexFragment and the merged fragment section managers of the previous fragments
        // (excluding the current fragment)
        let fragments_in_file: Vec<(FragSectionManager, DexFragment)> = fragments_in_file
            .into_iter()
            .map(|fragment| {
                let old_section_manager = fragment_section_manager;
                let new_section_manager =
                    fragment_section_manager.merge_fragment(fragment.get_section_manager());
                fragment_section_manager = new_section_manager;
                if !fragment.get_interfaces().is_empty() {
                    type_lists.insert(fragment.get_interfaces().clone(), 0);
                }
                (old_section_manager, fragment)
            })
            .collect();

        let mut section_manager: SectionManager = fragment_section_manager.into();

        section_manager.add_elt(Section::HeaderItem, None);
        section_manager.add_multiple_elt(Section::StringIdItem, strings.len());
        section_manager.add_multiple_elt(Section::TypeIdItem, type_ids.len());
        section_manager.add_multiple_elt(Section::ProtoIdItem, proto_ids.len());
        section_manager.add_multiple_elt(Section::FieldIdItem, field_ids.len());
        section_manager.add_multiple_elt(Section::MethodIdItem, method_ids.len());
        for string in &strings {
            section_manager.add_elt(Section::StringDataItem, Some(string.0.size()));
        }
        for proto in &proto_ids {
            if !proto.parameters.is_empty() {
                type_lists.insert(proto.parameters.clone(), 0);
            }
        }
        let type_list_list: Vec<TypeList> = type_lists
            .keys()
            .into_iter()
            .map(|ty_list| {
                if let Some(local_off) = type_lists.get_mut(ty_list) {
                    *local_off = section_manager.get_aligned_size(Section::TypeList)
                } else {
                    bail!("type list {ty_list:?} not needed but not found in dex_writter")
                }
                let ty_list = TypeList {
                    list: ty_list
                        .into_iter()
                        .map(|ty| {
                            index
                                .types
                                .get(ty)
                                .ok_or(anyhow!(
                                    "type {} needed but not found in dex writer",
                                    ty.__str__()
                                ))
                                .map(|idx| TypeItem {
                                    type_idx: *idx as u16,
                                })
                        })
                        .collect::<Result<_>>()?,
                };

                section_manager.add_elt(Section::TypeList, Some(ty_list.size()));
                Ok(ty_list)
            })
            .collect::<Result<_>>()?;

        section_manager.add_elt(Section::MapList, Some(4));
        let map_item_size = 12; /* = MapItem {
                                    type_: MapItemType::HeaderItem,
                                    unused: 0,
                                    size: 0,
                                    offset: 0,
                                }
                                .size(); */
        for section in Section::VARIANT_LIST {
            if !section.is_data() && section_manager.get_nb_elt(*section) != 0 {
                section_manager.incr_section_size(Section::MapList, map_item_size);
            }
        }
        let offsets = section_manager.get_offsets();
        // Sections are computed (except for class_data_item sizes, but those are at the end of the
        // file so it does not affect the map_list)

        let mut map_list = MapList::default();
        for section in Section::VARIANT_LIST {
            if !section.is_data() && section_manager.get_nb_elt(*section) != 0 {
                section_manager.incr_section_size(Section::MapList, map_item_size);
                map_list.list.push(MapItem {
                    type_: section.get_map_item_type(),
                    unused: 0,
                    size: section_manager.get_nb_elt(*section) as u32,
                    offset: offsets.get_offset(*section),
                });
            }
        }

        // link everything but class_def_item.class_data_off and collect the offsets of the
        // sections of the fragments
        let fragments_in_file: Vec<(SectionOffsets, DexFragment)> = fragments_in_file
            .into_iter()
            .map(|(frag_section_manager, fragment)| {
                let fragment_offsets = offsets.get_fragment_offset(&frag_section_manager);
                fragment.link_offsets_but_class_data(&fragment_offsets);
                todo!()
            })
            .collect();

        // TODO: class_def_item.class_data_off

        // TODO: serialize

        Ok(vec![])
    }
}

/// Index that associate descriptors to their id in the dex file.
#[derive(Debug, Clone)]
pub(crate) struct DexIndex<'a> {
    pub strings: HashMap<DexString, usize>,
    pub types: HashMap<IdType, usize>,
    pub protos: HashMap<IdMethodType, usize>,
    pub fields: HashMap<IdField, usize>,
    pub methods: HashMap<IdMethod, usize>,
    pub strings_list: &'a [DexString],
    pub type_ids_list: &'a [IdType],
    pub proto_ids_list: &'a [IdMethodType],
    pub field_ids_list: &'a [IdField],
    pub method_ids_list: &'a [IdMethod],
}

impl<'a> DexIndex<'a> {
    fn new(
        strings_list: &'a [DexString],
        type_ids_list: &'a [IdType],
        proto_ids_list: &'a [IdMethodType],
        field_ids_list: &'a [IdField],
        method_ids_list: &'a [IdMethod],
    ) -> Self {
        Self {
            strings_list,
            type_ids_list,
            proto_ids_list,
            field_ids_list,
            method_ids_list,
            strings: strings_list
                .iter()
                .cloned()
                .enumerate()
                .map(|(x, y)| (y, x))
                .collect(),
            types: type_ids_list
                .iter()
                .cloned()
                .enumerate()
                .map(|(x, y)| (y, x))
                .collect(),
            protos: proto_ids_list
                .iter()
                .cloned()
                .enumerate()
                .map(|(x, y)| (y, x))
                .collect(),
            fields: field_ids_list
                .iter()
                .cloned()
                .enumerate()
                .map(|(x, y)| (y, x))
                .collect(),
            methods: method_ids_list
                .iter()
                .cloned()
                .enumerate()
                .map(|(x, y)| (y, x))
                .collect(),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Section {
    HeaderItem,
    StringIdItem,
    TypeIdItem,
    ProtoIdItem,
    FieldIdItem,
    MethodIdItem,
    ClassDefItem,
    CallSiteIdItem,
    MethodHandleItem,
    Data,
    MapList,
    TypeList,
    AnnotationSetRefList,
    AnnotationSetItem,
    CodeItem,
    StringDataItem,
    DebugInfoItem,
    AnnotationItem,
    EncodedArrayItem,
    AnnotationsDirectoryItem,
    HiddenapiClassDataItem,
    ClassDataItem,
}

impl From<&FragSection> for Section {
    fn from(section: &FragSection) -> Self {
        match section {
            FragSection::ClassDefItem => Section::ClassDefItem,
            FragSection::CallSiteIdItem => Section::CallSiteIdItem,
            FragSection::MethodHandleItem => Section::MethodHandleItem,
            FragSection::AnnotationSetRefList => Section::AnnotationSetRefList,
            FragSection::AnnotationSetItem => Section::AnnotationSetItem,
            FragSection::CodeItem => Section::CodeItem,
            FragSection::StringDataItem => Section::StringDataItem,
            FragSection::DebugInfoItem => Section::DebugInfoItem,
            FragSection::AnnotationItem => Section::AnnotationItem,
            FragSection::EncodedArrayItem => Section::EncodedArrayItem,
            FragSection::AnnotationsDirectoryItem => Section::AnnotationsDirectoryItem,
            FragSection::ClassDataItem => Section::ClassDataItem,
            // FragSection::HiddenapiClassDataItem => Section::HiddenapiClassDataItem,
        }
    }
}

impl Section {
    const VARIANT_LIST: &'static [Self] = &[
        Self::HeaderItem,
        Self::StringIdItem,
        Self::TypeIdItem,
        Self::ProtoIdItem,
        Self::FieldIdItem,
        Self::MethodIdItem,
        Self::ClassDefItem,
        Self::CallSiteIdItem,
        Self::MethodHandleItem,
        Self::Data,
        Self::MapList,
        Self::TypeList,
        Self::AnnotationSetRefList,
        Self::AnnotationSetItem,
        Self::CodeItem,
        Self::StringDataItem,
        Self::DebugInfoItem,
        Self::AnnotationItem,
        Self::EncodedArrayItem,
        Self::AnnotationsDirectoryItem,
        Self::ClassDataItem, // must be last because contains offsets in Uleb,
        // so size change when linking !
        Self::HiddenapiClassDataItem,
    ];

    fn get_index(&self) -> usize {
        match self {
            Self::HeaderItem => 0,
            Self::StringIdItem => 1,
            Self::TypeIdItem => 2,
            Self::ProtoIdItem => 3,
            Self::FieldIdItem => 4,
            Self::MethodIdItem => 5,
            Self::ClassDefItem => 6,
            Self::CallSiteIdItem => 7,
            Self::MethodHandleItem => 8,
            Self::Data => 9,
            Self::MapList => 10,
            Self::TypeList => 11,
            Self::AnnotationSetRefList => 12,
            Self::AnnotationSetItem => 13,
            Self::CodeItem => 14,
            Self::StringDataItem => 15,
            Self::DebugInfoItem => 16,
            Self::AnnotationItem => 17,
            Self::EncodedArrayItem => 18,
            Self::AnnotationsDirectoryItem => 19,
            Self::ClassDataItem => 20,
            Self::HiddenapiClassDataItem => 21,
        }
    }

    fn get_default_elt_size(&self) -> Option<usize> {
        match self {
            Self::HeaderItem => Some(0x70),
            Self::StringIdItem => Some(4),
            Self::TypeIdItem => Some(4),
            Self::ProtoIdItem => Some(0xc),
            Self::FieldIdItem => Some(8),
            Self::MethodIdItem => Some(8),
            Self::ClassDefItem => Some(0x20),
            Self::CallSiteIdItem => Some(4),
            Self::MethodHandleItem => Some(8),
            Self::Data => panic!("element cannot be inserted in data dirctly"),
            Self::MapList => None,
            Self::TypeList => None,
            Self::AnnotationSetRefList => None,
            Self::AnnotationSetItem => None,
            Self::ClassDataItem => None,
            Self::CodeItem => None,
            Self::StringDataItem => None,
            Self::DebugInfoItem => None,
            Self::AnnotationItem => None,
            Self::EncodedArrayItem => None,
            Self::AnnotationsDirectoryItem => None,
            Self::HiddenapiClassDataItem => None,
        }
    }

    fn get_elt_size(&self, default_size: Option<usize>) -> usize {
        let fixed_size = self.get_default_elt_size();
        if let (Some(fixed_size), Some(default_size)) = (fixed_size, default_size) {
            if fixed_size == default_size {
                default_size
            } else {
                panic!(
                    "Element in {:?} have a size of {}, not {}",
                    self, fixed_size, default_size
                )
            }
        } else {
            fixed_size.or(default_size).unwrap_or_else(|| {
                panic!(
                    "Element of {:?} don't have a fixed size, you need to provide one",
                    self
                )
            })
        }
    }

    /// Return the previous section if it exist.
    fn prev(&self) -> Option<Self> {
        match self {
            Self::HeaderItem => None,
            Self::StringIdItem => Some(Self::HeaderItem),
            Self::TypeIdItem => Some(Self::StringIdItem),
            Self::ProtoIdItem => Some(Self::TypeIdItem),
            Self::FieldIdItem => Some(Self::ProtoIdItem),
            Self::MethodIdItem => Some(Self::FieldIdItem),
            Self::ClassDefItem => Some(Self::MethodIdItem),
            Self::CallSiteIdItem => Some(Self::ClassDefItem),
            Self::MethodHandleItem => Some(Self::CallSiteIdItem),
            Self::Data => Some(Self::MethodHandleItem),
            Self::MapList => Some(Self::MethodHandleItem), // Data is just an indicator
            Self::TypeList => Some(Self::MapList),
            Self::AnnotationSetRefList => Some(Self::TypeList),
            Self::AnnotationSetItem => Some(Self::AnnotationSetRefList),
            Self::CodeItem => Some(Self::AnnotationSetItem),
            Self::StringDataItem => Some(Self::CodeItem),
            Self::DebugInfoItem => Some(Self::StringDataItem),
            Self::AnnotationItem => Some(Self::DebugInfoItem),
            Self::EncodedArrayItem => Some(Self::AnnotationItem),
            Self::AnnotationsDirectoryItem => Some(Self::EncodedArrayItem),
            Self::ClassDataItem => Some(Self::AnnotationsDirectoryItem),
            Self::HiddenapiClassDataItem => Some(Self::ClassDataItem),
        }
    }

    fn get_map_item_type(&self) -> MapItemType {
        match self {
            Self::HeaderItem => MapItemType::HeaderItem,
            Self::StringIdItem => MapItemType::StringIdItem,
            Self::TypeIdItem => MapItemType::TypeIdItem,
            Self::ProtoIdItem => MapItemType::ProtoIdItem,
            Self::FieldIdItem => MapItemType::FieldIdItem,
            Self::MethodIdItem => MapItemType::MethodIdItem,
            Self::ClassDefItem => MapItemType::ClassDefItem,
            Self::CallSiteIdItem => MapItemType::CallSiteIdItem,
            Self::MethodHandleItem => MapItemType::MethodHandleItem,
            Self::Data => panic!("Data is not a MatItemType"),
            Self::MapList => MapItemType::MapList,
            Self::TypeList => MapItemType::TypeList,
            Self::AnnotationSetRefList => MapItemType::AnnotationSetRefList,
            Self::AnnotationSetItem => MapItemType::AnnotationSetItem,
            Self::CodeItem => MapItemType::CodeItem,
            Self::StringDataItem => MapItemType::StringDataItem,
            Self::DebugInfoItem => MapItemType::DebugInfoItem,
            Self::AnnotationItem => MapItemType::AnnotationItem,
            Self::EncodedArrayItem => MapItemType::EncodedArrayItem,
            Self::AnnotationsDirectoryItem => MapItemType::AnnotationsDirectoryItem,
            Self::ClassDataItem => MapItemType::ClassDataItem,
            Self::HiddenapiClassDataItem => MapItemType::HiddenapiClassDataItem,
        }
    }

    /// Return the alignment of the item in byte.
    fn get_item_alignment(&self) -> u32 {
        match self {
            Self::HeaderItem => 4,
            Self::StringIdItem => 4,
            Self::TypeIdItem => 4,
            Self::ProtoIdItem => 4,
            Self::FieldIdItem => 4,
            Self::MethodIdItem => 4,
            Self::ClassDefItem => 4,
            Self::CallSiteIdItem => 1,
            Self::MethodHandleItem => 4,
            Self::Data => 1,
            Self::MapList => 4,
            Self::TypeList => 4,
            Self::AnnotationSetRefList => 4,
            Self::AnnotationSetItem => 4,
            Self::CodeItem => 4,
            Self::StringDataItem => 1,
            Self::DebugInfoItem => 1,
            Self::AnnotationItem => 1,
            Self::EncodedArrayItem => 1,
            Self::AnnotationsDirectoryItem => 4,
            Self::ClassDataItem => 1,
            Self::HiddenapiClassDataItem => 1,
        }
    }

    fn is_data(&self) -> bool {
        matches!(self, Self::Data)
    }
}

#[derive(Debug, Default, Clone)]
struct SectionManager {
    sizes: [u32; Self::NB_SECTION],
    nb_elt: [usize; Self::NB_SECTION],
}

impl From<FragSectionManager> for SectionManager {
    fn from(frag_sections: FragSectionManager) -> Self {
        let mut section_manager = Self::default();
        for frag_section in FragSection::VARIANT_LIST {
            let section: Section = frag_section.into();
            section_manager.nb_elt[section.get_index()] += frag_sections.get_nb_elt(*frag_section);
            // Noticed the "aligned"
            section_manager.sizes[section.get_index()] = section_manager.get_aligned_size(section)
                + frag_sections.get_aligned_size(*frag_section);
        }
        section_manager
    }
}

impl SectionManager {
    const NB_SECTION: usize = 22;

    fn add_elt(&mut self, section: Section, size: Option<usize>) {
        if section.is_data() {
            panic!("Cannot add element directly in section data");
        }
        while self.sizes[section.get_index()] % section.get_item_alignment() != 0 {
            self.sizes[section.get_index()] += 1;
        }
        self.sizes[section.get_index()] += section.get_elt_size(size) as u32;
        self.nb_elt[section.get_index()] += 1;
    }

    /// Add `nb` element in `section`. The element must be of fixed size (the layout of the file
    /// ensure the element of fixed size are always aligned).
    fn add_multiple_elt(&mut self, section: Section, nb: usize) {
        if section.is_data() {
            panic!("Cannot add element directly in section data");
        }
        let size = if let Some(size) = section.get_default_elt_size() {
            size
        } else {
            panic!(
                "Cannot add multiple elements at the same time for element that don't \
            have a fixed size."
            );
        };
        self.sizes[section.get_index()] += (size * nb) as u32;
        self.nb_elt[section.get_index()] += nb;
    }

    fn incr_section_size(&mut self, section: Section, size: usize) {
        self.sizes[section.get_index()] += size as u32;
    }

    fn get_unaligned_size(&self, section: Section) -> u32 {
        if section.is_data() {
            self.sizes[section.get_index()..].iter().sum()
        } else {
            self.sizes[section.get_index()]
        }
    }

    /// The position of a potential new item in the section considering alignment.
    fn get_aligned_size(&self, section: Section) -> u32 {
        let mut size = self.get_unaligned_size(section);
        while size % section.get_item_alignment() != 0 {
            size += 1;
        }
        size
    }

    fn get_nb_elt(&self, section: Section) -> usize {
        self.nb_elt[section.get_index()]
    }

    /// Display the sections informations.
    #[allow(dead_code)]
    fn show(&self) {
        for section in Section::VARIANT_LIST {
            let size = self.get_unaligned_size(*section);
            let nb_elt = self.get_nb_elt(*section);
            println!("{section:?}: size: 0x{size:x}, nb elt: {nb_elt})");
        }
    }

    /// Return the offset of the sections with the sections defined in by this manager.
    fn get_offsets(&self) -> SectionOffsets {
        let mut offsets = SectionOffsets::default();
        let mut offset = 0;
        for section in Section::VARIANT_LIST {
            while offset % section.get_item_alignment() != 0 {
                offset += 1;
            }
            offsets.offsets[section.get_index()];
            offset += self.sizes[section.get_index()];
        }
        offsets
    }
}

#[derive(Debug, Default, Clone)]
pub(crate) struct SectionOffsets {
    offsets: [u32; SectionManager::NB_SECTION],
}

impl SectionOffsets {
    pub fn get_offset(&self, section: Section) -> u32 {
        self.offsets[section.get_index()]
    }

    /// Return the offset of the sections **of a fragment**, from the **start of the dex file**.
    /// `previous_fragment_sections` is are the merged `FragSectionManager` of the previous
    /// fragment.
    fn get_fragment_offset(
        &self,
        previous_fragment_sections: &FragSectionManager,
    ) -> SectionOffsets {
        let mut offsets = self.clone();
        for frag_section in FragSection::VARIANT_LIST {
            let section: Section = frag_section.into();
            // Noticed the "aligned"
            offsets.offsets[section.get_index()] +=
                previous_fragment_sections.get_aligned_size(*frag_section);
        }
        offsets
    }
}
