/// definitions about e_type field.
mod elf_type;
pub use elf_type::*;

/// definitions about e_machine field.
mod machine;
pub use machine::*;

/// definitions about elf_class field.
mod class;
pub use class::*;

/// definitions about elf_data field.
mod data;
pub use data::*;

/// the 64-bit ELF Header.
#[derive(Default)]
pub struct Elf64Header {
    /// ELF identification
    pub ident: ElfIdentification,
    /// object file type
    pub elf_type: ElfType,
    /// architecture
    pub machine: ElfMachine,
    /// object file version
    pub version: Elf64Word,
    /// entry point virtual address
    pub entry: Elf64Addr,
    /// program header table file offset
    pub phoff: Elf64Off,
    /// section header table file offset
    pub shoff: Elf64Off,
    /// processor-specific flags
    pub flags: Elf64Word,
    /// ELF header size in bytes
    pub ehsize: Elf64Half,
    /// program header table entry size
    pub phentsize: Elf64Half,
    /// program header table entry count
    pub phnum: Elf64Half,
    /// section header table entry size
    pub shentsize: Elf64Half,
    /// section header table entry count
    pub shnum: Elf64Half,
    /// section header string table index
    pub shstrndx: Elf64Half,
}

impl From<RawElf64Header> for Elf64Header {
    fn from(raw: RawElf64Header) -> Self {
        Self {
            ident: ElfIdentification::from(raw.e_ident),
            elf_type: ElfType::from(raw.e_type),
            machine: ElfMachine::from(raw.e_machine),
            version: raw.e_version,
            entry: raw.e_entry,
            phoff: raw.e_phoff,
            shoff: raw.e_shoff,
            flags: raw.e_flags,
            ehsize: raw.e_ehsize,
            phentsize: raw.e_phentsize,
            phnum: raw.e_phnum,
            shentsize: raw.e_shentsize,
            shnum: raw.e_shnum,
            shstrndx: raw.e_shstrndx,
        }
    }
}

/// the raw representation of the 64-bit ELF Header.
#[derive(Default)]
pub struct RawElf64Header {
    /// ELF identification
    pub e_ident: RawElfIdentification,
    /// object file type
    pub e_type: Elf64Half,
    /// architecture
    pub e_machine: Elf64Half,
    /// object file version
    pub e_version: Elf64Word,
    /// entry point virtual address
    pub e_entry: Elf64Addr,
    /// program header table file offset
    pub e_phoff: Elf64Off,
    /// section header table file offset
    pub e_shoff: Elf64Off,
    /// processor-specific flags
    pub e_flags: Elf64Word,
    /// ELF header size in bytes
    pub e_ehsize: Elf64Half,
    /// program header table entry size
    pub e_phentsize: Elf64Half,
    /// program header table entry count
    pub e_phnum: Elf64Half,
    /// section header table entry size
    pub e_shentsize: Elf64Half,
    /// section header table entry count
    pub e_shnum: Elf64Half,
    /// section header string table index
    pub e_shstrndx: Elf64Half,
}

/// ELF identification.
/// please reference the section "ELF Identification" in Tool Interface Standard (TIS) Executable
/// and Linkable Format (ELF) Specification 1.2.
/// <https://refspecs.linuxbase.org/>
#[derive(Default)]
pub struct ElfIdentification {
    /// identities the file's class or capacity.
    pub class: ElfClass,
    /// the data encoding of the processor-specific data in the object file.
    pub data: ElfData,
    /// the ELF header version number.
    pub version: u8,
}

impl From<RawElfIdentification> for ElfIdentification {
    fn from(raw: RawElfIdentification) -> Self {
        Self {
            class: ElfClass::from(raw.e_class),
            data: ElfData::from(raw.e_data),
            version: raw.e_version,
        }
    }
}

/// the raw version of ELF identification.
/// please reference the section "ELF Identification" in Tool Interface Standard (TIS) Executable
/// and Linkable Format (ELF) Specification 1.2.
/// <https://refspecs.linuxbase.org/>
pub struct RawElfIdentification {
    /// holds a magic number for identifying the file as an ELF.
    pub e_magic: [u8; ELF_MAGICNUMBER_FIELD_LENGTH],
    /// identities the file's class or capacity.
    pub e_class: u8,
    /// the data encoding of the processor-specific data in the object file.
    pub e_data: u8,
    /// the ELF header version number.
    pub e_version: u8,
    /// the unused bytes in e_ident.
    /// these bytes are reserved and set to zero.
    pub e_padding: [u8; ELF_IDENT_PADDING_FIELD_LENGTH],
}

impl Default for RawElfIdentification {
    fn default() -> Self {
        Self {
            e_magic: [0x00; ELF_MAGICNUMBER_FIELD_LENGTH],
            e_class: 0,
            e_data: 0,
            e_version: 0,
            e_padding: [0x00; ELF_IDENT_PADDING_FIELD_LENGTH],
        }
    }
}

mod magicnumber {
    /// the signature "\x7fELF" of the ELF magic number.
    pub const ELF_MAGICNUMBER_SIGNATURE: [u8; 4] = [0x7f, 0x45, 0x4c, 0x46];
    /// the byte-length of ELF magic number field in e_ident.
    pub const ELF_MAGICNUMBER_FIELD_LENGTH: usize = 4;

    /// the index in e_ident that is the first-byte of the ELF magic number.
    pub const ELF_IDENT_MAGICNUMBER0_INDEX: usize = 0;
    /// the index in e_ident that is the second-byte of the ELF magic number.
    pub const ELF_IDENT_MAGICNUMBER1_INDEX: usize = 1;
    /// the index in e_ident that is the third-byte of the ELF magic number.
    pub const ELF_IDENT_MAGICNUMBER2_INDEX: usize = 2;
    /// the index in e_ident that is the fourth-byte of the ELF magic number.
    pub const ELF_IDENT_MAGICNUMBER3_INDEX: usize = 3;
}
pub use magicnumber::*;

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

/// the e_version index in e_ident.
pub const ELF_IDENT_VERSION_INDEX: usize = 6;
/// the beginning offset of the e_padding field in e_ident.
pub const ELF_IDENT_PADDING_INDEX: usize = 7;
/// the byte-length of the e_padding field in e_ident.
pub const ELF_IDENT_PADDING_FIELD_LENGTH: usize = 9;
