#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HartStartError {
    Failed(isize),
    NotSupported,
    InvalidParam,
    InvalidAddress,
    AlreadyAvailable,
    AlreadyStarted,
}

pub fn hart_start(hartid: usize, start_addr: usize, opaque: usize) -> Result<(), HartStartError> {
    let ret = sbi_rt::hart_start(hartid, start_addr, opaque);
    match ret.error {
        x if x == sbi_rt::SbiRet::success(0).error => Ok(()),
        x if x == sbi_rt::SbiRet::not_supported().error => Err(HartStartError::NotSupported),
        x if x == sbi_rt::SbiRet::invalid_param().error => Err(HartStartError::InvalidParam),
        x if x == sbi_rt::SbiRet::invalid_address().error => Err(HartStartError::InvalidAddress),
        x if x == sbi_rt::SbiRet::already_available().error => {
            Err(HartStartError::AlreadyAvailable)
        }
        x if x == sbi_rt::SbiRet::already_started().error => Err(HartStartError::AlreadyStarted),
        other => Err(HartStartError::Failed(other as isize)),
    }
}

pub fn set_timer(stime_value: u64) -> Result<(), isize> {
    let ret = sbi_rt::set_timer(stime_value);
    if ret.error == 0 {
        Ok(())
    } else {
        Err(ret.error as isize)
    }
}

pub fn system_reset_shutdown() -> Result<(), isize> {
    let ret = sbi_rt::system_reset(sbi_rt::Shutdown, sbi_rt::NoReason);
    if ret.error == 0 {
        Ok(())
    } else {
        Err(ret.error as isize)
    }
}

pub fn system_reset_reboot() -> Result<(), isize> {
    let ret = sbi_rt::system_reset(sbi_rt::ColdReboot, sbi_rt::NoReason);
    if ret.error == 0 {
        Ok(())
    } else {
        Err(ret.error as isize)
    }
}

pub fn detect_timebase_frequency() -> Option<usize> {
    let fdt_ptr = crate::fdt::fdt_addr()?;
    let fdt = unsafe { fdt_raw::Fdt::from_ptr(fdt_ptr).ok()? };
    let cpus = fdt.find_by_path("/cpus")?;
    let prop = cpus.find_property("timebase-frequency")?;
    prop.as_u32().map(|value| value as usize)
}
