//! Host IRQ facade for AxVM runtime glue.

#[cfg(test)]
use core::sync::atomic::{AtomicUsize, Ordering};

use super::arceos;

pub(crate) type IrqContext = arceos::ArceOsIrqContext;
pub(crate) type IrqError = arceos::ArceOsIrqError;
pub(crate) type IrqId = arceos::ArceOsIrqId;
pub(crate) type IrqReturn = arceos::ArceOsIrqReturn;
pub(crate) type IrqSource = arceos::ArceOsIrqSource;

pub(crate) fn make_irq_id(domain: u16, hwirq: u32) -> IrqId {
    arceos::make_irq_id(domain, hwirq)
}

pub(crate) fn request_shared_irq(
    irq: IrqId,
    handler: impl FnMut(IrqContext) -> IrqReturn + Send + 'static,
) -> Result<arceos::ArceOsIrqHandle, arceos::ArceOsIrqError> {
    arceos::request_shared_irq(irq, handler)
}

#[cfg(test)]
static TEST_ENABLED_IRQ_RAW: AtomicUsize = AtomicUsize::new(usize::MAX);

pub(crate) fn set_host_irq_enable(irq: IrqId, enabled: bool) -> Result<(), IrqError> {
    set_host_irq_enable_impl(irq, enabled)
}

#[cfg(test)]
fn set_host_irq_enable_impl(irq: IrqId, enabled: bool) -> Result<(), IrqError> {
    if enabled {
        TEST_ENABLED_IRQ_RAW.store(irq_to_test_raw(irq), Ordering::Release);
    } else if TEST_ENABLED_IRQ_RAW.load(Ordering::Acquire) == irq_to_test_raw(irq) {
        TEST_ENABLED_IRQ_RAW.store(usize::MAX, Ordering::Release);
    }
    Ok(())
}

#[cfg(not(test))]
fn set_host_irq_enable_impl(irq: IrqId, enabled: bool) -> Result<(), IrqError> {
    arceos::set_irq_enable(irq, enabled)
}

pub(crate) fn resolve_irq_source(source: IrqSource) -> Result<IrqId, arceos::ArceOsIrqError> {
    arceos::resolve_irq_source(source)
}

#[cfg(test)]
fn irq_to_test_raw(irq: IrqId) -> usize {
    (usize::from(irq.domain.0) << 32) | irq.hwirq.0 as usize
}

#[cfg(test)]
pub(crate) fn reset_test_irq_enable_state() {
    TEST_ENABLED_IRQ_RAW.store(usize::MAX, Ordering::Release);
}

#[cfg(test)]
pub(crate) fn test_irq_is_enabled(irq: IrqId) -> bool {
    TEST_ENABLED_IRQ_RAW.load(Ordering::Acquire) == irq_to_test_raw(irq)
}
