use std::str;
use thiserror::Error;
use zerocopy::FromBytes;
use zerocopy::Immutable;
use zerocopy::KnownLayout;
use super::netlink::*;
const ACPI_EVENT_SIZE: usize = std::mem::size_of::<AcpiGenlEvent>();
const GENL_HDRLEN: usize = std::mem::size_of::<GenlMsgHdr>();
const NLA_HDRLEN: usize = std::mem::size_of::<NlAttr>();
#[derive(Error, Debug)]
pub enum AcpiEventError {
    #[error("GenmsghdrCmd or NlAttrType inappropriate for acpi event")]
    TypeAttrMissmatch,
    #[error("Something goes wrong: msg_len {0} is not correct")]
    InvalidMsgLen(usize),
}
type Result<T> = std::result::Result<T, AcpiEventError>;
#[allow(dead_code)]
enum NlAttrType {
    AcpiGenlAttrUnspec,
    AcpiGenlAttrEvent, AcpiGenlAttrMax,
}
#[allow(dead_code)]
enum GenmsghdrCmd {
    AcpiGenlCmdUnspec,
    AcpiGenlCmdEvent, AcpiGenlCmdMax,
}
#[repr(C)]
#[derive(Copy, Clone, FromBytes, Immutable, KnownLayout)]
struct AcpiGenlEvent {
    device_class: [u8; 20],
    bus_id: [u8; 15],
    _type: u32,
    data: u32,
}
pub struct AcpiNotifyEvent {
    pub device_class: String,
    pub bus_id: String,
    pub _type: u32,
    pub data: u32,
}
impl AcpiNotifyEvent {
    pub fn new(netlink_message: NetlinkMessage) -> Result<Self> {
        let msg_len = netlink_message.data.len();
        if msg_len != GENL_HDRLEN + NLA_HDRLEN + ACPI_EVENT_SIZE {
            return Err(AcpiEventError::InvalidMsgLen(msg_len));
        }
        let (genl_hdr, nl_attr) = GenlMsgHdr::read_from_prefix(netlink_message.data)
            .expect("unable to get GenlMsgHdr from slice");
        let (nl_attr, body) =
            NlAttr::read_from_prefix(nl_attr).expect("unable to get NlAttr from slice");
        if genl_hdr.cmd != GenmsghdrCmd::AcpiGenlCmdEvent as u8
            || nl_attr._type != NlAttrType::AcpiGenlAttrEvent as u16
        {
            return Err(AcpiEventError::TypeAttrMissmatch);
        }
        let acpi_event =
            AcpiGenlEvent::read_from_bytes(body).expect("unable to get AcpiGenlEvent from slice");
        Ok(AcpiNotifyEvent {
            device_class: strip_padding(&acpi_event.device_class).to_owned(),
            bus_id: strip_padding(&acpi_event.bus_id).to_owned(),
            _type: acpi_event._type,
            data: acpi_event.data,
        })
    }
}
fn strip_padding(b: &[u8]) -> &str {
    let pos = b
        .iter()
        .position(|&c| c == 0)
        .expect("`b` doesn't contain any nul bytes");
    str::from_utf8(&b[..pos]).unwrap()
}