use moa_cpumask::CpuId;

use crate::{
    irq::{irq_enter, irq_exit, irqflags::local_daif_mask},
    println,
    processor::{cpu_relax, this_processor_id},
    sched::schedule::schedule_ipi,
    smp::{CPU_ONLINE, smp_call::smp_call_function_in_interrupt},
};

pub(crate) const IPI_RESCHEDULE: u32 = 0;
pub(crate) const IPI_CALL_FUNC: u32 = 1;
pub(crate) const IPI_CPU_STOP: u32 = 2;

fn ipi_cpu_stop(cpu: CpuId) {
    CPU_ONLINE.clear(cpu);
    local_daif_mask();
    loop {
        cpu_relax();
    }
}

pub(crate) fn handle_ipi(ipirn: u32) {
    let cpu = this_processor_id();

    match ipirn {
        IPI_RESCHEDULE => {
            schedule_ipi();
        },
        IPI_CALL_FUNC => {
            irq_enter();
            smp_call_function_in_interrupt();
            irq_exit();
        },
        IPI_CPU_STOP => {
            irq_enter();
            ipi_cpu_stop(cpu);
            irq_exit();
        },
        _ => {
            println!("CPU{}: Unknown IPI message {:#x}", cpu.raw(), ipirn);
        },
    }
}
