use crate::{Elf64Addr, Elf64Off, Elf64Word, Elf64Xword};

/// the set of the raw representation section data.
pub type RawElf64Sections = Vec<RawElf64Section>;
/// the raw representation section data.
pub type RawElf64Section = Vec<u8>;

/// the raw representation of section header table.
#[derive(Default, Debug)]
pub struct RawElf64SectionHeaderTable {
    /// the actual sequence of section headers.
    _headers: Vec<RawElf64SectionHeader>,
}

impl RawElf64SectionHeaderTable {
    /// the length of the section header table.
    pub fn len(&self) -> usize {
        self._headers.len()
    }

    /// checks the table has no entries.
    pub fn is_empty(&self) -> bool {
        self._headers.is_empty()
    }

    /// set the given section-header iterator into itself for building.
    pub fn headers<T>(mut self, it: T) -> Self
    where
        T: IntoIterator<Item = RawElf64SectionHeader>,
    {
        self._headers = it.into_iter().collect();
        self
    }

    /// returns a program-header iterator.
    pub fn iter(&self) -> impl Iterator<Item = &RawElf64SectionHeader> {
        self._headers.iter()
    }
}

impl std::iter::IntoIterator for RawElf64SectionHeaderTable {
    type Item = RawElf64SectionHeader;
    type IntoIter = std::vec::IntoIter<RawElf64SectionHeader>;

    fn into_iter(self) -> Self::IntoIter {
        self._headers.into_iter()
    }
}

impl std::convert::From<RawElf64SectionHeaderTable> for Vec<RawElf64SectionHeader> {
    fn from(value: RawElf64SectionHeaderTable) -> Self {
        value._headers
    }
}

impl std::convert::From<Vec<RawElf64SectionHeader>> for RawElf64SectionHeaderTable {
    fn from(value: Vec<RawElf64SectionHeader>) -> Self {
        Self { _headers: value }
    }
}

/// The raw representation of elf64 section header.
#[derive(Clone, Default, Debug)]
pub struct RawElf64SectionHeader {
    /// section name (string table index)
    pub sh_name: Elf64Word,
    /// section type
    pub sh_type: Elf64Word,
    /// section flags
    pub sh_flags: Elf64Xword,
    /// section virtual addr at execution
    pub sh_addr: Elf64Addr,
    /// section file offset
    pub sh_offset: Elf64Off,
    /// section size in byte
    pub sh_size: Elf64Xword,
    /// link to another section
    pub sh_link: Elf64Word,
    /// additional section information
    pub sh_info: Elf64Word,
    /// section alignment
    pub sh_addralign: Elf64Xword,
    /// entry size if section holds table
    pub sh_entsize: Elf64Xword,
}

impl RawElf64SectionHeader {
    /// set the given sh_nameinto itself for building.
    pub fn name(mut self, sh_name: Elf64Word) -> Self {
        self.sh_name = sh_name;
        self
    }

    /// set the given sh_type into itself for building.
    pub fn section_type(mut self, sh_type: Elf64Word) -> Self {
        self.sh_type = sh_type;
        self
    }

    /// set the given sh_flags nto itself for building.
    pub fn flags(mut self, sh_flags: Elf64Xword) -> Self {
        self.sh_flags = sh_flags;
        self
    }

    /// set the given sh_offset into itself for building.
    pub fn offset(mut self, sh_offset: Elf64Off) -> Self {
        self.sh_offset = sh_offset;
        self
    }

    /// set the given sh_addr into itself for building.
    pub fn addr(mut self, sh_addr: Elf64Addr) -> Self {
        self.sh_addr = sh_addr;
        self
    }

    /// set the given sh_size into itself for building.
    pub fn size(mut self, sh_size: Elf64Xword) -> Self {
        self.sh_size = sh_size;
        self
    }

    /// set the given sh_link into itself for building.
    pub fn link(mut self, sh_link: Elf64Word) -> Self {
        self.sh_link = sh_link;
        self
    }

    /// set the given sh_info into itself for building.
    pub fn info(mut self, sh_info: Elf64Word) -> Self {
        self.sh_info = sh_info;
        self
    }

    /// set the given sh_addralign into itself for building.
    pub fn addralign(mut self, sh_addralign: Elf64Xword) -> Self {
        self.sh_addralign = sh_addralign;
        self
    }

    /// set the given sh_entsize into itself for building.
    pub fn entsize(mut self, sh_entsize: Elf64Xword) -> Self {
        self.sh_entsize = sh_entsize;
        self
    }
}
