use crate::Elf64Half;

/// indicates the file has no file type.
pub const ELF_TYPE_NONE: Elf64Half = 0;
/// indicates the file is relocatable.
pub const ELF_TYPE_RELOCATABLE: Elf64Half = 1;
/// indicates the file is executable.
pub const ELF_TYPE_EXECUTABLE: Elf64Half = 2;
/// indicates the file is shared object.
pub const ELF_TYPE_DYNAMIC: Elf64Half = 3;
/// indicates the file is core-file.
pub const ELF_TYPE_CORE: Elf64Half = 4;
/// the number of pre-declared elf_type values.
pub const ELF_TYPE_NUM: Elf64Half = 5;
/// the range start of OS-specific values.
pub const ELF_TYPE_LOOS: Elf64Half = 0xfe00;
/// the range end of OS-specific values.
pub const ELF_TYPE_HIOS: Elf64Half = 0xfeff;
/// the range start of Processor-specific values.
pub const ELF_TYPE_LOPROC: Elf64Half = 0xff00;
/// the range endof Processor-specific values.
pub const ELF_TYPE_HIPROC: Elf64Half = 0xffff;

/// object file type
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum ElfType {
    /// no file type
    None,
    /// relocatable
    Rel,
    /// executable
    Exec,
    /// shared object file
    Dyn,
    /// core file
    Core,
    /// the number of the pre-declared e_type values.
    Num,
    /// custom value
    Any(Elf64Half),
}

impl std::fmt::Display for ElfType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let s = match self {
            Self::None => "None".to_string(),
            Self::Rel => "Relocatable".to_string(),
            Self::Exec => "Executable".to_string(),
            Self::Dyn => "Dynanic".to_string(),
            Self::Core => "Core".to_string(),
            Self::Num => format!("Unknown!({})", ELF_TYPE_NUM),
            Self::Any(v) => format!("Unknown!({})", v),
        };

        write!(f, "{}", s)
    }
}

impl Default for ElfType {
    fn default() -> Self {
        Self::None
    }
}

impl From<Elf64Half> for ElfType {
    fn from(value: Elf64Half) -> Self {
        match value {
            ELF_TYPE_NONE => Self::None,
            ELF_TYPE_RELOCATABLE => Self::Rel,
            ELF_TYPE_EXECUTABLE => Self::Exec,
            ELF_TYPE_DYNAMIC => Self::Dyn,
            ELF_TYPE_CORE => Self::Core,
            ELF_TYPE_NUM => Self::Num,
            _ => Self::Any(value),
        }
    }
}
