use super::ElfParser;
use crate::section;

impl ElfParser {
    pub(super) fn parse_raw_elf64_sections<'a>(
        &'a self,
        b: &'a [u8],
        shdrs: &section::RawElf64SectionHeaderTable,
    ) -> nom::IResult<&'a [u8], section::RawElf64Sections> {
        let sections = shdrs
            .iter()
            .map(|shdr| {
                let start = shdr.sh_offset as usize;
                let end = shdr.sh_offset as usize + shdr.sh_size as usize;
                b[start..end].to_vec()
            })
            .collect();

        Ok((b, sections))
    }

    pub(super) fn parse_raw_elf64_section_header_table<'a>(
        &'a self,
        b: &'a [u8],
        encoding: u8,
        shoff: usize,
        shnum: usize,
    ) -> nom::IResult<&'a [u8], section::RawElf64SectionHeaderTable> {
        let mut shdrs = vec![section::RawElf64SectionHeader::default(); shnum];

        for (i, shdr) in shdrs.iter_mut().enumerate().take(shnum) {
            let start = shoff + (std::mem::size_of::<section::RawElf64SectionHeader>() * i);
            let (_b, v) = self.parse_raw_elf64_section_header(&b[start..], encoding)?;
            *shdr = v;
        }

        let end = shoff + (std::mem::size_of::<section::RawElf64SectionHeader>() * shnum);
        Ok((
            &b[end..],
            section::RawElf64SectionHeaderTable::default().headers(shdrs),
        ))
    }

    fn parse_raw_elf64_section_header<'a>(
        &'a self,
        b: &'a [u8],
        encoding: u8,
    ) -> nom::IResult<&'a [u8], section::RawElf64SectionHeader> {
        let (b, sh_name) = self.parse_elf64_word(encoding, b)?;
        let (b, sh_type) = self.parse_elf64_word(encoding, b)?;
        let (b, sh_flags) = self.parse_elf64_xword(encoding, b)?;
        let (b, sh_addr) = self.parse_elf64_address(encoding, b)?;
        let (b, sh_offset) = self.parse_elf64_offset(encoding, b)?;
        let (b, sh_size) = self.parse_elf64_xword(encoding, b)?;
        let (b, sh_link) = self.parse_elf64_word(encoding, b)?;
        let (b, sh_info) = self.parse_elf64_word(encoding, b)?;
        let (b, sh_addralign) = self.parse_elf64_xword(encoding, b)?;
        let (b, sh_entsize) = self.parse_elf64_xword(encoding, b)?;

        let shdr = section::RawElf64SectionHeader::default()
            .name(sh_name)
            .section_type(sh_type)
            .flags(sh_flags)
            .addr(sh_addr)
            .offset(sh_offset)
            .size(sh_size)
            .link(sh_link)
            .info(sh_info)
            .addralign(sh_addralign)
            .entsize(sh_entsize);
        Ok((b, shdr))
    }
}

#[cfg(test)]
mod tests {
    use crate::{header, parser::ElfParserConfig, section};

    #[test]
    fn test_parse_raw_elf64_section_header() {
        let input = vec![
            0x0b, 0x00, 0x00, 0x00, // sh_name
            0x01, 0x00, 0x00, 0x00, // sh_type
            0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sh_flags
            0x18, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sh_addr
            0x18, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sh_offset
            0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sh_size
            0x00, 0x00, 0x00, 0x00, // sh_link
            0x00, 0x00, 0x00, 0x00, // sh_info
            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sh_addralign
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sh_entsize
        ];

        let p = ElfParserConfig::new().build();
        let result = p.parse_raw_elf64_section_header(&input, header::ELF_IDENT_DATA_2LSB);
        assert!(result.is_ok());

        let (b, shdr) = result.unwrap();
        assert!(b.is_empty());

        assert_eq!(0xb, shdr.sh_name);
        assert_eq!(0x1, shdr.sh_type);
        assert_eq!(section::ELF_SECTION_HEADER_FLAG_ALLOC, shdr.sh_flags);
        assert_eq!(0x318, shdr.sh_addr);
        assert_eq!(0x318, shdr.sh_offset);
        assert_eq!(0x1c, shdr.sh_size);
        assert_eq!(0, shdr.sh_link);
        assert_eq!(0, shdr.sh_info);
        assert_eq!(0x1, shdr.sh_addralign);
        assert_eq!(0, shdr.sh_entsize);
    }
}
