use super::{ElfParser, ElfParserError};
use crate::{file, header, program_header, section};

impl ElfParser {
    /// parses the entire of the given ELF file.
    pub fn parse_elf_file<'a>(
        &'a self,
        b: &'a [u8],
    ) -> Result<file::ElfFile, ElfParserError<&'a [u8]>> {
        let (b, elf_identification) = self
            .peek_raw_elf_identification(b)
            .map_err(ElfParserError::FailedToParse)?;

        match elf_identification.e_class {
            header::ELF_IDENT_CLASS_64 => self.parse_elf64_file(b).map(file::ElfFile::Elf64),
            _ => Err(ElfParserError::Unsupported(format!(
                "invalid e_ident.e_class {}",
                elf_identification.e_class
            ))),
        }
    }

    /// parses the entire of the given ELF file.
    /// that may return the "raw" representation of the ELF file.
    pub fn parse_raw_elf_file<'a>(
        &'a self,
        b: &'a [u8],
    ) -> Result<file::RawElfFile, ElfParserError<&'a [u8]>> {
        let (b, elf_identification) = self
            .peek_raw_elf_identification(b)
            .map_err(ElfParserError::FailedToParse)?;

        match elf_identification.e_class {
            header::ELF_IDENT_CLASS_64 => self.parse_raw_elf64_file(b).map(file::RawElfFile::Elf64),
            _ => Err(ElfParserError::Unsupported(format!(
                "invalid e_ident.e_class {}",
                elf_identification.e_class
            ))),
        }
    }

    /// parses an 64-bit ELF file.
    pub(super) fn parse_elf64_file<'a>(
        &'a self,
        b: &'a [u8],
    ) -> Result<file::Elf64File, ElfParserError<&'a [u8]>> {
        let (_b, ehdr) = if self.parse_config._parse_header {
            self.parse_elf64_header(b)
                .map_err(ElfParserError::FailedToParse)?
        } else {
            (b, header::Elf64Header::default())
        };

        let (_b, phdrs) = if self.parse_config._parse_pht {
            self.parse_elf64_program_header_table(
                b,
                ehdr.ident.data.into(),
                ehdr.phoff as usize,
                ehdr.phnum as usize,
            )
            .map_err(ElfParserError::FailedToParse)?
        } else {
            (b, program_header::Elf64ProgramHeaderTable::default())
        };

        Ok(file::Elf64File {
            header: ehdr,
            program_headers: phdrs,
        })
    }

    /// parses an 64-bit ELF file as the raw representation.
    pub(super) fn parse_raw_elf64_file<'a>(
        &'a self,
        b: &'a [u8],
    ) -> Result<file::RawElf64File, ElfParserError<&'a [u8]>> {
        let (_b, ehdr) = if self.parse_config._parse_header {
            self.parse_raw_elf64_header(b)
                .map_err(ElfParserError::FailedToParse)?
        } else {
            (b, header::RawElf64Header::default())
        };

        let (_b, shdrs) = if self.parse_config._parse_sht {
            self.parse_raw_elf64_section_header_table(
                b,
                ehdr.e_ident.e_data,
                ehdr.e_shoff as usize,
                ehdr.e_shnum as usize,
            )
            .map_err(ElfParserError::FailedToParse)?
        } else {
            (b, section::RawElf64SectionHeaderTable::default())
        };

        let (_b, section_data_set) = if self.parse_config._parse_sections {
            self.parse_raw_elf64_sections(b, &shdrs)
                .map_err(ElfParserError::FailedToParse)?
        } else {
            (b, vec![section::RawElf64Section::default(); shdrs.len()])
        };

        let (_b, phdrs) = if self.parse_config._parse_pht {
            self.parse_raw_elf64_program_header_table(
                b,
                ehdr.e_ident.e_data,
                ehdr.e_phoff as usize,
                ehdr.e_phnum as usize,
            )
            .map_err(ElfParserError::FailedToParse)?
        } else {
            (b, program_header::RawElf64ProgramHeaderTable::default())
        };

        Ok(file::RawElf64File {
            header: ehdr,
            program_headers: phdrs,
            section_headers: shdrs,
            sections: section_data_set,
        })
    }
}
