//! PE Rich Header module
use std::marker::PhantomData;

use lief_ffi as ffi;

use crate::{common::FromFFI, declare_iterator};


/// Structure which represents the not-so-documented rich header
///
/// This structure is usually located at the end of the [`crate::pe::Binary::dos_stub`]
/// and contains information about the build environment.
/// It is generated by the Microsoft linker `link.exe` and there are no options to disable
/// or remove this information.
pub struct RichHeader<'a> {
    ptr: cxx::UniquePtr<ffi::PE_RichHeader>,
    _owner: PhantomData<&'a ffi::PE_Binary>,
}

impl std::fmt::Debug for RichHeader<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("RichHeader")
            .field("key", &self.key())
            .finish()
    }
}

impl<'a> FromFFI<ffi::PE_RichHeader> for RichHeader<'a> {
    fn from_ffi(ptr: cxx::UniquePtr<ffi::PE_RichHeader>) -> Self {
        RichHeader {
            ptr,
            _owner: PhantomData,
        }
    }
}

impl<'a> RichHeader<'a> {
    /// Key used to encode the header (xor operation)
    pub fn key(&self) -> u32 {
        self.ptr.key()
    }

    /// Return an iterator over the [`RichEntry`] within the header
    pub fn entries(&self) -> Entries {
        Entries::new(self.ptr.entries())
    }
}

pub struct RichEntry<'a> {
    ptr: cxx::UniquePtr<ffi::PE_RichEntry>,
    _owner: PhantomData<&'a ffi::PE_RichHeader>,
}

impl RichEntry<'_> {

    /// Entry type
    pub fn id(&self) -> u16 {
        self.ptr.id()
    }

    /// Build number of the tool (if any)
    pub fn build_id(&self) -> u16 {
        self.ptr.build_id()
    }

    /// *Occurrence* count.
    pub fn count(&self) -> u32 {
        self.ptr.count()
    }
}

impl std::fmt::Debug for RichEntry<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("RichEntry")
            .field("id", &self.id())
            .field("build_id", &self.build_id())
            .field("count", &self.count())
            .finish()
    }
}

impl<'a> FromFFI<ffi::PE_RichEntry> for RichEntry<'a> {
    fn from_ffi(ptr: cxx::UniquePtr<ffi::PE_RichEntry>) -> Self {
        RichEntry {
            ptr,
            _owner: PhantomData,
        }
    }
}

declare_iterator!(
    Entries,
    RichEntry<'a>,
    ffi::PE_RichEntry,
    ffi::PE_RichHeader,
    ffi::PE_RichHeader_it_entries
);
