use alloc::sync::Arc;
use yggdrasil_abi::error::Error;

use crate::device::Device;

#[derive(Clone)]
pub struct IrqHandle {
    pub irq: Irq,
    pub options: IrqOptions,
    pub intc: Arc<dyn ExternalInterruptController>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Irq {
    Private(u32),
    External(u32),
}

#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u32)]
pub enum IrqLevel {
    #[default]
    Default,
    ActiveHigh,
    ActiveLow,
}

#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u32)]
pub enum IrqTrigger {
    #[default]
    Default,
    Edge,
    Level,
}

#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
pub struct IrqOptions {
    pub level: IrqLevel,
    pub trigger: IrqTrigger,
}

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum IpiDeliveryTarget {
    Specific(usize),
    ThisCpu,
    OtherCpus,
}

#[derive(Clone, Copy, Debug, Default)]
pub struct MsiInfo {
    pub address: usize,
    pub value: u32,
    pub vector: usize,
    pub affinity: InterruptAffinity,
}

#[derive(Clone, Copy, Debug, Default)]
pub enum InterruptAffinity {
    #[default]
    Any,
    Specific(usize),
}

#[derive(Clone, Copy, Debug)]
pub enum IrqVector {
    Msi(usize),
    Irq(Irq),
}

/// Describes messages sent from some CPU to others
#[derive(Clone, Copy, PartialEq, Debug)]
#[repr(u64)]
pub enum IpiMessage {
    /// Indicates that the sender CPU entered kernel panic and wants other CPUs to follow
    Panic,
    /// Indicates that the cores should either halt and wait for the caller to shut the system
    /// down, or they should shut down by themselves, depending on the platform
    Shutdown,
}

pub trait InterruptHandler: Device {
    fn handle_irq(self: Arc<Self>, vector: IrqVector) -> bool;
}

pub trait InterruptTable: Sync {
    fn handler(&self, index: usize) -> Option<&Arc<dyn InterruptHandler>>;
}

pub trait ExternalInterruptController: Device {
    /// Performs IRQ delivery method configuration and registers a handler to execute when it is
    /// fired
    fn register_irq(
        &self,
        irq: Irq,
        options: IrqOptions,
        handler: Arc<dyn InterruptHandler>,
    ) -> Result<(), Error>;

    /// Enables the specified IRQ (unmasks it)
    fn enable_irq(&self, irq: Irq) -> Result<(), Error>;

    /// Handles a single pending interrupt on this controller.
    /// The function is intended for interrupt controllers which have internal registers to track
    /// the IRQ index and the order of interrupt handling (if multiple are handled in sequence) is
    /// platform/controller specific.
    fn handle_pending_irqs(&self) {}

    /// Handles a single pending interrupt with a known index on this controller.
    /// The function is intended for interrupt controllers where vectors "know" their interrupt
    /// index.
    fn handle_specific_irq(&self, index: usize) {
        let _ = index;
    }
}

pub trait MessageInterruptController: Device {
    fn register_msi(
        self: Arc<Self>,
        affinity: InterruptAffinity,
        handler: Arc<dyn InterruptHandler>,
    ) -> Result<MsiInfo, Error> {
        let mut range = [MsiInfo {
            affinity,
            ..Default::default()
        }];
        self.register_msi_range(&mut range, handler)?;
        Ok(range[0])
    }

    #[allow(unused)]
    fn register_msi_range(
        self: Arc<Self>,
        range: &mut [MsiInfo],
        handler: Arc<dyn InterruptHandler>,
    ) -> Result<(), Error> {
        Err(Error::NotImplemented)
    }

    fn handle_msi(&self, #[allow(unused)] vector: usize) {}
}

pub trait LocalInterruptController: Device {
    /// Initializes the local interrupt controller for an Application Processor instance.
    ///
    /// # Safety
    ///
    /// The caller must ensure this function is only called once per each AP (and only for APs).
    unsafe fn init_ap(&self) -> Result<(), Error>;

    fn send_ipi(&self, target: IpiDeliveryTarget, msg: IpiMessage) -> Result<(), Error>;
}

pub struct FixedInterruptTable<const N: usize> {
    rows: [Option<Arc<dyn InterruptHandler>>; N],
}

impl IrqHandle {
    pub fn register(&self, handler: Arc<dyn InterruptHandler>) -> Result<(), Error> {
        self.intc.register_irq(self.irq, self.options, handler)
    }

    pub fn enable(&self) -> Result<(), Error> {
        self.intc.enable_irq(self.irq)
    }
}

impl<const N: usize> FixedInterruptTable<N> {
    pub const fn new() -> Self {
        Self {
            rows: [const { None }; N],
        }
    }

    pub fn insert(&mut self, index: usize, entry: Arc<dyn InterruptHandler>) -> Result<(), Error> {
        let row = self.rows.get_mut(index).ok_or(Error::InvalidArgument)?;
        if row.is_some() {
            return Err(Error::AlreadyExists);
        }
        *row = Some(entry);
        Ok(())
    }

    pub fn insert_least_loaded(
        &mut self,
        handler: Arc<dyn InterruptHandler>,
    ) -> Result<usize, Error> {
        let index = self
            .rows
            .iter()
            .position(|p| p.is_none())
            .ok_or(Error::InvalidArgument)?;
        self.rows[index].replace(handler);
        Ok(index)
    }
}

impl<const N: usize> InterruptTable for FixedInterruptTable<N> {
    #[inline]
    fn handler(&self, index: usize) -> Option<&Arc<dyn InterruptHandler>> {
        self.rows.get(index).and_then(|p| p.as_ref())
    }
}

impl IrqLevel {
    pub fn override_default(self, value: IrqLevel) -> Self {
        match self {
            Self::Default => value,
            _ => self,
        }
    }
}

impl IrqTrigger {
    pub fn override_default(self, value: IrqTrigger) -> Self {
        match self {
            Self::Default => value,
            _ => self,
        }
    }
}
