/// utilities for parsing the entire ELF files.
mod file;
/// utilities for parsing the ELF headers.
mod header;
/// utilities for parsing the ELF Program Headers.
mod program_header;
/// utilities for parsing the ELF section header/data.
mod section;

/// errors that the parser uses
mod error;
pub use error::*;

use crate::{
    header::{ELF_IDENT_DATA_2LSB, ELF_IDENT_DATA_2MSB},
    Elf64Half, Elf64Off, Elf64Word, Elf64Xword,
};

/// An 32-bit/64-bit ELF parser.
#[derive(Default)]
pub struct ElfParser {
    /// parse_config holds the configuration of the parser.
    pub(crate) parse_config: ElfParserConfig,
}

impl ElfParser {
    /// parses Elf64Half(u16) bytes.
    pub(crate) fn parse_elf64_half<'a>(
        &'a self,
        encoding: u8,
        b: &'a [u8],
    ) -> nom::IResult<&'a [u8], Elf64Half> {
        self.parse_elf64_u16(encoding, b)
    }

    /// parses Elf64Word(u32) bytes.
    pub(crate) fn parse_elf64_word<'a>(
        &'a self,
        encoding: u8,
        b: &'a [u8],
    ) -> nom::IResult<&'a [u8], Elf64Word> {
        self.parse_elf64_u32(encoding, b)
    }

    /// parses Elf64Off(u64) bytes.
    pub(crate) fn parse_elf64_offset<'a>(
        &'a self,
        encoding: u8,
        b: &'a [u8],
    ) -> nom::IResult<&'a [u8], Elf64Off> {
        self.parse_elf64_u64(encoding, b)
    }

    /// parses Elf64Addr(u64) bytes.
    pub(crate) fn parse_elf64_address<'a>(
        &'a self,
        encoding: u8,
        b: &'a [u8],
    ) -> nom::IResult<&'a [u8], Elf64Off> {
        self.parse_elf64_u64(encoding, b)
    }

    /// parses Elf64Xword(u64) bytes.
    pub(crate) fn parse_elf64_xword<'a>(
        &'a self,
        encoding: u8,
        b: &'a [u8],
    ) -> nom::IResult<&'a [u8], Elf64Xword> {
        self.parse_elf64_u64(encoding, b)
    }

    /// parses u16 bytes in ELF.
    fn parse_elf64_u16<'a>(&'a self, encoding: u8, b: &'a [u8]) -> nom::IResult<&'a [u8], u16> {
        match encoding {
            ELF_IDENT_DATA_2LSB => nom::number::complete::le_u16(b),
            ELF_IDENT_DATA_2MSB => nom::number::complete::be_u16(b),
            _ => unreachable!(),
        }
    }

    /// parses u32 bytes in ELF.
    fn parse_elf64_u32<'a>(&'a self, encoding: u8, b: &'a [u8]) -> nom::IResult<&'a [u8], u32> {
        match encoding {
            ELF_IDENT_DATA_2LSB => nom::number::complete::le_u32(b),
            ELF_IDENT_DATA_2MSB => nom::number::complete::be_u32(b),
            _ => unreachable!(),
        }
    }

    /// parses u64 bytes in ELF.
    fn parse_elf64_u64<'a>(&'a self, encoding: u8, b: &'a [u8]) -> nom::IResult<&'a [u8], u64> {
        match encoding {
            ELF_IDENT_DATA_2LSB => nom::number::complete::le_u64(b),
            ELF_IDENT_DATA_2MSB => nom::number::complete::be_u64(b),
            _ => unreachable!(),
        }
    }
}

/// A configuration that determines what the parser parses/how the parser parses.
pub struct ElfParserConfig {
    /// determines whether the parser tries to parse ELF header or not.
    pub(crate) _parse_header: bool,
    /// determines whether the parser tries to parse ELF program header table or not.
    pub(crate) _parse_pht: bool,
    /// determines whether the parser tries to parse ELF section header table or not.
    pub(crate) _parse_sht: bool,
    /// determines whether the parser tries to parse ELF section (data) or not.
    pub(crate) _parse_sections: bool,
}

impl ElfParserConfig {
    /// creates a new ElfParserConfig.
    pub fn new() -> Self {
        Self {
            _parse_header: true,
            _parse_pht: true,
            _parse_sht: true,
            _parse_sections: true,
        }
    }

    /// build an ElfParser from the configuration.
    pub fn build(self) -> ElfParser {
        ElfParser { parse_config: self }
    }

    /// determines whether the parse tries to parse ELF header or not.
    pub fn parse_header(mut self, parse_header: bool) -> Self {
        self._parse_header = parse_header;
        self
    }

    /// determines whether the parse tries to parse ELF program header table or not.
    pub fn parse_program_header_table(mut self, parse_pht: bool) -> Self {
        self._parse_pht = parse_pht;
        self
    }

    /// determines whether the parse tries to parse ELF section header table or not.
    pub fn parse_section_header_table(mut self, parse_sht: bool) -> Self {
        self._parse_sht = parse_sht;
        self
    }

    /// determines whether the parse tries to parse ELF section (data) or not.
    pub fn parse_sections(mut self, parse_sections: bool) -> Self {
        self._parse_sections = parse_sections;
        self
    }
}

impl Default for ElfParserConfig {
    fn default() -> Self {
        Self {
            _parse_header: true,
            _parse_pht: true,
            _parse_sht: true,
            _parse_sections: true,
        }
    }
}
