mod codegen;
mod relocations;
pub use codegen::*;
pub use relocations::*;
use crate::{ElfParser, StringTable, VariantStructBinaryDeserialize};
use binary_serde::{binary_serde_bitfield, BinarySerde, BitfieldBitOrder, Endianness};
use elflib_macros::{define_raw_struct_by_variants, define_raw_struct_generic_bitlen};
pub const ELF_MAGIC: &[u8] = &[0x7f, b'E', b'L', b'F'];
pub const EI_NIDENT: usize = 16;
pub const ELF_IDENT_PADDING_SIZE: usize = EI_NIDENT - ElfIdentHeader::SERIALIZED_SIZE;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct DebugIgnore<T>(pub(crate) T);
impl<T> core::fmt::Debug for DebugIgnore<T> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "...")
    }
}
impl<T> From<T> for DebugIgnore<T> {
    fn from(value: T) -> Self {
        Self(value)
    }
}
impl<T> core::ops::Deref for DebugIgnore<T> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
impl<T> core::ops::DerefMut for DebugIgnore<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub struct ElfFileInfo {
    pub endianness: Endianness,
    pub bit_length: ArchBitLength,
    pub os_abi: OsAbi,
    pub arch: Architechture,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash, BinarySerde)]
pub struct ElfIdentHeader {
    pub magic: [u8; ELF_MAGIC.len()],
    pub bit_size: ArchBitLength,
    pub endianness: ElfEndianness,
    pub elf_version: ElfVersionInIdent,
    pub os_abi: OsAbi,
    pub abi_version: AbiVersion,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash, BinarySerde)]
pub struct ElfIdent {
    pub header: ElfIdentHeader,
    pub padding: [u8; ELF_IDENT_PADDING_SIZE],
}
define_raw_struct_by_variants! {
    struct ProgramHeader32 {
        ty: ProgramHeaderType,
        offset: u32,
        virt_addr: u32,
        phys_addr: u32,
        size_in_file: u32,
        size_in_memory: u32,
        flags: ProgramHeaderFlags,
        alignment: u32,
    }
    struct ProgramHeader64 {
        ty: ProgramHeaderType,
        flags: ProgramHeaderFlags,
        offset: u64,
        virt_addr: u64,
        phys_addr: u64,
        size_in_file: u64,
        size_in_memory: u64,
        alignment: u64,
    }
    => ()
}
define_raw_struct_by_variants! {
    struct SectionHeader64 {
        name_offset: u32,
        ty: SectionHeaderType,
        flags: SectionHeaderFlags,
        address: u64,
        offset: u64,
        size: u64,
        link: u32,
        info: u32,
        address_alignemnt: u64,
        entry_size: u64,
    }
    struct SectionHeader32 {
        name_offset: u32,
        ty: SectionHeaderType,
        flags: SectionHeaderFlagsU32,
        address: u32,
        offset: u32,
        size: u32,
        link: u32,
        info: u32,
        address_alignemnt: u32,
        entry_size: u32,
    }
    => ()
}
define_raw_struct_generic_bitlen! {
    struct ElfHeader {
        ident: ElfIdent,
        ty: ElfFileType,
        arch: Architechture,
        version: ElfVersion,
        entry: U,
        program_headers_off: U,
        section_headers_off: U,
        flags: ElfFlags,
        header_size: u16,
        program_header_entry_size: u16,
        program_headers_amount: u16,
        section_header_entry_size: u16,
        section_headers_amount: u16,
        section_names_section_index: u16,
    }
    => ()
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
#[binary_serde_bitfield(order = BitfieldBitOrder::LsbFirst)]
pub struct SymbolInfo {
    #[bits(4)]
    pub ty: SymbolType,
    #[bits(4)]
    pub binding: SymbolBinding,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
#[binary_serde_bitfield(order = BitfieldBitOrder::LsbFirst)]
pub struct SymbolOtherInfo {
    #[bits(2)]
    pub visibility: SymbolVisibility,
    #[bits(6)]
    pub padding: u8,
}
#[derive(Debug, Clone)]
pub struct SymbolRefContext<'a> {
    pub(crate) string_table: StringTable<'a>,
}
define_raw_struct_by_variants! {
    struct Symbol32 {
        name_index_in_string_table: u32,
        value: u32,
        size: u32,
        info: SymbolInfo,
        other_info: SymbolOtherInfo,
        related_section_index: u16,
    }
    struct Symbol64 {
        name_index_in_string_table: u32,
        info: SymbolInfo,
        other_info: SymbolOtherInfo,
        related_section_index: u16,
        value: u64,
        size: u64,
    }
    => SymbolRefContext<'a>
}
#[derive(Debug, BinarySerde, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum AbiVersion {
    Valid = 0,
}
#[derive(Debug, BinarySerde, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum ElfVersionInIdent {
    Current = 1,
}
#[derive(Debug, BinarySerde, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum ElfVersion {
    Current = 1,
}
#[derive(Debug, BinarySerde, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum ElfEndianness {
    Little = 1,
    Big = 2,
}
impl From<ElfEndianness> for binary_serde::Endianness {
    fn from(value: ElfEndianness) -> Self {
        match value {
            ElfEndianness::Little => Endianness::Little,
            ElfEndianness::Big => Endianness::Big,
        }
    }
}
impl From<binary_serde::Endianness> for ElfEndianness {
    fn from(value: binary_serde::Endianness) -> Self {
        match value {
            Endianness::Little => ElfEndianness::Little,
            Endianness::Big => ElfEndianness::Big,
        }
    }
}
#[derive(Debug, BinarySerde, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum ArchBitLength {
    Arch32Bit = 1,
    Arch64Bit = 2,
}
macro_rules! gen_enum_size_truncating_wrapper {
    {$wrapper_name: ident, $inner_ty: ty, $truncated_uint: ty, $original_uint: ty, $convert_inner_to_bits_input_var_name: ident, $convert_inner_to_bits_body: block} => {
        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
        pub struct $wrapper_name($inner_ty);
        impl BinarySerde for $wrapper_name {
            const SERIALIZED_SIZE: usize = <$truncated_uint as BinarySerde>::SERIALIZED_SIZE;
            type RecursiveArray = <$truncated_uint as BinarySerde>::RecursiveArray;
            fn binary_serialize(&self, buf: &mut [u8], endianness: Endianness) {
                let converted = {
                    let $convert_inner_to_bits_input_var_name = &self.0;
                    $convert_inner_to_bits_body
                };
                converted.binary_serialize(buf, endianness)
            }
            fn binary_deserialize(
                buf: &[u8],
                endianness: Endianness,
            ) -> Result<Self, binary_serde::DeserializeError> {
                let value = <$truncated_uint>::binary_deserialize(buf, endianness)?;
                let bytes = (value as $original_uint).binary_serialize_to_array(endianness);
                Ok(Self(<$inner_ty>::binary_deserialize(
                    bytes.as_ref(),
                    endianness,
                )?))
            }
        }
        impl TryFrom<$inner_ty> for $wrapper_name {
            type Error = <$truncated_uint as TryFrom<$original_uint>>::Error;
            fn try_from(value: $inner_ty) -> Result<Self, Self::Error> {
                let converted = {
                    let $convert_inner_to_bits_input_var_name = &value;
                    $convert_inner_to_bits_body
                };
                let _ = <$truncated_uint>::try_from(converted)?;
                Ok(Self(value))
            }
        }
        impl From<$wrapper_name> for $inner_ty {
            fn from(value: $wrapper_name) -> Self {
                value.0
            }
        }
    };
}
gen_enum_size_truncating_wrapper! {RelocationTypeU8, RelocationType, u8, u32, x, {*x as u32}}
gen_enum_size_truncating_wrapper! {SectionHeaderFlagsU32, SectionHeaderFlags, u32, u64, x, {x.bits()}}