use core::fmt::Debug;

use alloc::{
    string::{String, ToString},
    sync::{Arc, Weak},
    vec::Vec,
};
use hashbrown::HashMap;
use log::{info, warn};
use system_error::SystemError;

use crate::{
    driver::{base::device::Device, open_firmware::device_node::DeviceNode},
    exception::{irqdata::IrqLineStatus, irqdesc::irq_desc_manager, manage::irq_manager},
    libs::{
        rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard},
        spinlock::SpinLock,
    },
};

use super::{
    dummychip::no_irq_chip,
    irqchip::{IrqChip, IrqChipData, IrqChipGeneric, IrqGcFlags},
    irqdata::{IrqData, IrqHandlerData},
    irqdesc::{IrqDesc, IrqFlowHandler},
    HardwareIrqNumber, IrqNumber,
};

static mut IRQ_DOMAIN_MANAGER: Option<Arc<IrqDomainManager>> = None;

/// 获取中断域管理器的引用
#[inline(always)]
pub fn irq_domain_manager() -> &'static Arc<IrqDomainManager> {
    unsafe { IRQ_DOMAIN_MANAGER.as_ref().unwrap() }
}

pub(super) fn irq_domain_manager_init() {
    unsafe {
        IRQ_DOMAIN_MANAGER = Some(Arc::new(IrqDomainManager::new()));
    }
}
/// 中断域管理器
pub struct IrqDomainManager {
    domains: SpinLock<Vec<Arc<IrqDomain>>>,
    inner: RwLock<InnerIrqDomainManager>,
}

impl IrqDomainManager {
    pub fn new() -> IrqDomainManager {
        IrqDomainManager {
            domains: SpinLock::new(Vec::new()),
            inner: RwLock::new(InnerIrqDomainManager {
                default_domain: None,
            }),
        }
    }

    /// 创建一个新的线性映射的irqdomain, 并将其添加到irqdomain管理器中
    ///
    /// 创建的irqdomain，中断号是线性的，即从0开始，依次递增
    ///
    /// ## 参数
    ///
    /// - `name` - 中断域的名字
    /// - `ops` - 中断域的操作
    /// - `irq_size` - 中断号的数量
    #[allow(dead_code)]
    pub fn create_and_add_linear(
        &self,
        name: String,
        ops: &'static dyn IrqDomainOps,
        irq_size: u32,
    ) -> Option<Arc<IrqDomain>> {
        self.create_and_add(
            name,
            ops,
            IrqNumber::new(0),
            HardwareIrqNumber::new(0),
            irq_size,
        )
    }

    /// 创建一个新的irqdomain, 并将其添加到irqdomain管理器中
    ///
    /// ## 参数
    ///
    /// - `name` - 中断域的名字
    /// - `ops` - 中断域的操作
    /// - `first_irq` - 起始软件中断号
    /// - `first_hwirq` - 起始硬件中断号
    /// - `irq_size` - 中断号的数量
    ///
    /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/kernel/irq/irqdomain.c?fi=__irq_domain_add#139
    pub fn create_and_add(
        &self,
        name: String,
        ops: &'static dyn IrqDomainOps,
        first_irq: IrqNumber,
        first_hwirq: HardwareIrqNumber,
        irq_size: u32,
    ) -> Option<Arc<IrqDomain>> {
        let domain = IrqDomain::new(
            None,
            Some(name),
            ops,
            IrqDomainFlags::NAME_ALLOCATED,
            IrqDomainBusToken::Any,
            first_irq + irq_size,
            first_hwirq + irq_size,
        )?;

        self.add_domain(domain.clone());

        return Some(domain);
    }

    fn add_domain(&self, domain: Arc<IrqDomain>) {
        self.domains.lock_irqsave().push(domain);
    }

    #[allow(dead_code)]
    pub fn remove_domain(&self, domain: &Arc<IrqDomain>) {
        let mut domains = self.domains.lock_irqsave();
        let index = domains
            .iter()
            .position(|x| Arc::ptr_eq(x, domain))
            .expect("domain not found");
        domains.remove(index);
    }

    /// 获取默认的中断域
    #[allow(dead_code)]
    pub fn default_domain(&self) -> Option<Arc<IrqDomain>> {
        self.inner.read().default_domain.clone()
    }

    /// 设置默认的中断域
    ///
    /// 在创建IRQ映射的时候，如果没有指定中断域，就会使用默认的中断域
    pub fn set_default_domain(&self, domain: Arc<IrqDomain>) {
        self.inner.write_irqsave().default_domain = Some(domain);
    }

    /// 将指定范围的硬件中断号与软件中断号一一对应的关联起来
    ///
    /// ## 参数
    ///
    /// - `domain` - 中断域
    /// - `first_irq` - 起始软件中断号
    /// - `first_hwirq` - 起始硬件中断号
    /// - `count` - 数量
    pub fn domain_associate_many(
        &self,
        domain: &Arc<IrqDomain>,
        first_irq: IrqNumber,
        first_hwirq: HardwareIrqNumber,
        count: u32,
    ) {
        for i in 0..count {
            if let Err(e) = self.domain_associate(domain, first_irq + i, first_hwirq + i) {
                warn!("domain associate failed: {:?}, domain '{:?}' didn't like hwirq {} to virq {} mapping.", e, domain.name(), (first_hwirq + i).data(), (first_irq + i).data());
            }
        }
    }

    /// 将一个硬件中断号与一个软件中断号关联起来
    ///
    /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/kernel/irq/irqdomain.c#562
    pub fn domain_associate(
        &self,
        domain: &Arc<IrqDomain>,
        irq: IrqNumber,
        hwirq: HardwareIrqNumber,
    ) -> Result<(), SystemError> {
        if hwirq >= domain.revmap.read_irqsave().hwirq_max {
            warn!(
                "hwirq {} is out of range for domain {:?}",
                hwirq.data(),
                domain.name()
            );
            return Err(SystemError::EINVAL);
        }
        let irq_data = irq_desc_manager()
            .lookup(irq)
            .ok_or_else(|| {
                warn!("irq_desc not found for irq {}", irq.data());
                SystemError::EINVAL
            })?
            .irq_data();
        if irq_data.domain().is_some() {
            warn!(
                "irq {} is already associated with domain {:?}",
                irq.data(),
                irq_data.domain().unwrap().name()
            );
            return Err(SystemError::EINVAL);
        }

        let mut irq_data_guard = irq_data.inner();
        irq_data_guard.set_hwirq(hwirq);
        irq_data_guard.set_domain(Some(domain.clone()));
        drop(irq_data_guard);
        let r = domain.ops.map(domain, hwirq, irq);
        if let Err(e) = r {
            if e != SystemError::ENOSYS {
                if e != SystemError::EPERM {
                    info!("domain associate failed: {:?}, domain '{:?}' didn't like hwirq {} to virq {} mapping.", e, domain.name(), hwirq.data(), irq.data());
                }
                let mut irq_data_guard = irq_data.inner();
                irq_data_guard.set_domain(None);
                irq_data_guard.set_hwirq(HardwareIrqNumber::new(0));
                return Err(e);
            }
        }

        if domain.name().is_none() {
            let chip = irq_data.chip_info_read_irqsave().chip();
            domain.set_name(chip.name().to_string());
        }

        self.irq_domain_set_mapping(domain, hwirq, irq_data);

        irq_manager().irq_clear_status_flags(irq, IrqLineStatus::IRQ_NOREQUEST)?;

        return Ok(());
    }

    fn irq_domain_set_mapping(
        &self,
        domain: &Arc<IrqDomain>,
        hwirq: HardwareIrqNumber,
        irq_data: Arc<IrqData>,
    ) {
        if domain.no_map() {
            return;
        }

        domain.revmap.write_irqsave().insert(hwirq, irq_data);
    }
    /// 递归调用 domain_ops->activate 以激活中断
    ///
    /// ## 参数
    ///
    /// - irq_data: 与中断关联的最外层 irq_data
    /// - reserve: 如果为true，则仅预留一个中断向量，而不是分配一个
    ///
    /// 这是调用 domain_ops->activate 以编程中断控制器的第二步，以便中断实际上可以被传递。
    pub fn activate_irq(&self, irq_data: &Arc<IrqData>, reserve: bool) -> Result<(), SystemError> {
        let mut r = Ok(());
        // debug!(
        //     "activate_irq: irq_data.common_data().status().is_activated()={}",
        //     irq_data.common_data().status().is_activated()
        // );
        if !irq_data.common_data().status().is_activated() {
            r = self.do_activate_irq(Some(irq_data.clone()), reserve);
        }

        if r.is_err() {
            irq_data.common_data().status().set_activated();
        }

        return r;
    }

    #[inline(never)]
    fn do_activate_irq(
        &self,
        irq_data: Option<Arc<IrqData>>,
        reserve: bool,
    ) -> Result<(), SystemError> {
        let mut r = Ok(());

        if let Some(irq_data) = irq_data {
            // debug!("do_activate_irq: irq_data={:?}", irq_data);
            if let Some(domain) = irq_data.domain() {
                // debug!("do_activate_irq: domain={:?}", domain.name());
                let parent_data = irq_data.parent_data().and_then(|x| x.upgrade());
                if let Some(parent_data) = parent_data.clone() {
                    r = self.do_activate_irq(Some(parent_data), reserve);
                }

                if r.is_err() {
                    let tmpr = domain.ops.activate(&domain, &irq_data, reserve);
                    if let Err(e) = tmpr {
                        if e != SystemError::ENOSYS && parent_data.is_some() {
                            self.do_deactivate_irq(parent_data);
                        }
                    }
                }
            }
        }

        return r;
    }
    #[allow(clippy::only_used_in_recursion)]
    fn do_deactivate_irq(&self, irq_data: Option<Arc<IrqData>>) {
        if let Some(irq_data) = irq_data {
            if let Some(domain) = irq_data.domain() {
                domain.ops.deactivate(&domain, &irq_data);
                let pp = irq_data.parent_data().and_then(|x| x.upgrade());

                if pp.is_some() {
                    self.do_deactivate_irq(pp);
                }
            }
        }
    }

    /// `irq_domain_set_info` - 在 @domain 中为 @virq 设置完整的数据
    ///
    /// ## 参数
    ///
    /// - `domain`: 要匹配的中断域
    /// - `virq`: IRQ号
    /// - `hwirq`: 硬件中断号
    /// - `chip`: 相关的中断芯片
    /// - `chip_data`: 相关的中断芯片数据
    /// - `handler`: 中断流处理器
    /// - `handler_data`: 中断流处理程序数据
    /// - `handler_name`: 中断处理程序名称
    #[allow(clippy::too_many_arguments)]
    #[allow(dead_code)]
    pub fn domain_set_info(
        &self,
        domain: &Arc<IrqDomain>,
        virq: IrqNumber,
        hwirq: HardwareIrqNumber,
        chip: Arc<dyn IrqChip>,
        chip_data: Option<Arc<dyn IrqChipData>>,
        flow_handler: &'static dyn IrqFlowHandler,
        handler_data: Option<Arc<dyn IrqHandlerData>>,
        handler_name: Option<String>,
    ) {
        let r = self.domain_set_hwirq_and_chip(domain, virq, hwirq, Some(chip), chip_data);
        if r.is_err() {
            return;
        }
        irq_manager().__irq_set_handler(virq, flow_handler, false, handler_name);
        irq_manager().irq_set_handler_data(virq, handler_data).ok();
    }

    /// `domain_set_hwirq_and_chip` - 在 @domain 中为 @virq 设置 hwirq 和 irqchip
    ///
    /// ## 参数
    ///
    /// - `domain`: 要匹配的中断域
    /// - `virq`: IRQ号
    /// - `hwirq`: hwirq号
    /// - `chip`: 相关的中断芯片
    /// - `chip_data`: 相关的芯片数据
    pub fn domain_set_hwirq_and_chip(
        &self,
        domain: &Arc<IrqDomain>,
        virq: IrqNumber,
        hwirq: HardwareIrqNumber,
        chip: Option<Arc<dyn IrqChip>>,
        chip_data: Option<Arc<dyn IrqChipData>>,
    ) -> Result<(), SystemError> {
        let irq_data: Arc<IrqData> = self
            .domain_get_irq_data(domain, virq)
            .ok_or(SystemError::ENOENT)?;
        let mut inner = irq_data.inner();
        let mut chip_info = irq_data.chip_info_write_irqsave();

        inner.set_hwirq(hwirq);
        if let Some(chip) = chip {
            chip_info.set_chip(Some(chip));
        } else {
            chip_info.set_chip(Some(no_irq_chip()));
        };

        chip_info.set_chip_data(chip_data);

        return Ok(());
    }

    /// `irq_domain_get_irq_data` - 获取与 @virq 和 @domain 关联的 irq_data
    ///
    /// ## 参数
    ///
    /// - `domain`: 要匹配的域
    /// - `virq`: 要获取 irq_data 的IRQ号
    pub fn domain_get_irq_data(
        &self,
        domain: &Arc<IrqDomain>,
        virq: IrqNumber,
    ) -> Option<Arc<IrqData>> {
        let desc = irq_desc_manager().lookup(virq)?;
        let mut irq_data = Some(desc.irq_data());

        while irq_data.is_some() {
            let dt = irq_data.unwrap();
            if dt.domain().is_some() && Arc::ptr_eq(dt.domain().as_ref().unwrap(), domain) {
                return Some(dt);
            }
            irq_data = dt.parent_data().and_then(|x| x.upgrade());
        }

        return None;
    }

    /// `resolve_irq_mapping` - 从硬件中断号找到中断号。
    ///
    /// ## 参数
    ///
    /// - `domain`: 拥有此硬件中断的域
    /// - `hwirq`: 该域空间中的硬件中断号
    /// - `irq`: 如果需要，可选的指针以返回Linux中断
    ///
    /// ## 返回
    ///
    /// 返回一个元组，包含中断描述符和中断号
    pub fn resolve_irq_mapping(
        &self,
        mut domain: Option<Arc<IrqDomain>>,
        hwirq: HardwareIrqNumber,
    ) -> Result<(Arc<IrqDesc>, IrqNumber), SystemError> {
        if domain.is_none() {
            domain = Some(self.default_domain().ok_or(SystemError::EINVAL)?);
        }

        let domain = domain.unwrap();

        if domain.no_map() {
            if hwirq < domain.revmap_read_irqsave().hwirq_max {
                let irq_desc = irq_desc_manager()
                    .lookup(IrqNumber::new(hwirq.data()))
                    .ok_or(SystemError::EINVAL)?;
                if irq_desc.irq_data().hardware_irq() == hwirq {
                    let irq = irq_desc.irq_data().irq();
                    return Ok((irq_desc, irq));
                }
            }

            return Err(SystemError::EINVAL);
        }

        let revmap = domain.revmap_read_irqsave();
        let irq_data = revmap.lookup(hwirq).ok_or(SystemError::EINVAL)?;
        let irq_desc = irq_data.irq_desc().unwrap();
        return Ok((irq_desc, irq_data.irq()));
    }
}

struct InnerIrqDomainManager {
    default_domain: Option<Arc<IrqDomain>>,
}

/// 中断域
///
/// 用于把硬件中断号翻译为软件中断号的映射的对象
///
/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#164
#[allow(dead_code)]
#[derive(Debug)]
pub struct IrqDomain {
    /// 中断域的名字 (二选一)
    name: Option<&'static str>,
    allocated_name: SpinLock<Option<String>>,
    /// 中断域的操作
    ops: &'static dyn IrqDomainOps,
    inner: SpinLock<InnerIrqDomain>,
    /// 中断号反向映射
    revmap: RwLock<IrqDomainRevMap>,
}

#[allow(dead_code)]
#[derive(Debug)]
struct InnerIrqDomain {
    /// this field not touched by the core code
    host_data: Option<Arc<dyn IrqChipData>>,
    /// host per irq_domain flags
    flags: IrqDomainFlags,
    /// The number of mapped interrupts
    mapcount: u32,
    bus_token: IrqDomainBusToken,
    /// 指向 generic chip 列表的指针。
    /// 有一个辅助函数用于为中断控制器驱动程序设置一个或
    /// 多个 generic chip，该函数使用此指针并依赖于 generic chip 库。
    generic_chip: Option<Arc<IrqDomainChipGeneric>>,
    /// Pointer to a device that the domain represent, and that will be
    /// used for power management purposes.
    device: Option<Arc<dyn Device>>,
    /// Pointer to parent irq_domain to support hierarchy irq_domains
    parent: Option<Weak<IrqDomain>>,
}

impl IrqDomain {
    #[allow(dead_code)]
    pub fn new(
        name: Option<&'static str>,
        allocated_name: Option<String>,
        ops: &'static dyn IrqDomainOps,
        flags: IrqDomainFlags,
        bus_token: IrqDomainBusToken,
        irq_max: IrqNumber,
        hwirq_max: HardwareIrqNumber,
    ) -> Option<Arc<Self>> {
        if name.is_none() && allocated_name.is_none() {
            return None;
        }

        let x = IrqDomain {
            name,
            allocated_name: SpinLock::new(allocated_name),
            ops,
            inner: SpinLock::new(InnerIrqDomain {
                host_data: None,
                flags,
                mapcount: 0,
                bus_token,
                generic_chip: None,
                device: None,
                parent: None,
            }),
            revmap: RwLock::new(IrqDomainRevMap {
                map: HashMap::new(),
                hwirq_max,
                irq_max,
            }),
        };

        return Some(Arc::new(x));
    }

    /// 中断域是否不对中断号进行转换
    pub fn no_map(&self) -> bool {
        self.inner
            .lock_irqsave()
            .flags
            .contains(IrqDomainFlags::NO_MAP)
    }

    #[allow(dead_code)]
    fn revmap_read_irqsave(&self) -> RwLockReadGuard<IrqDomainRevMap> {
        self.revmap.read_irqsave()
    }

    #[allow(dead_code)]
    fn revmap_write_irqsave(&self) -> RwLockWriteGuard<IrqDomainRevMap> {
        self.revmap.write_irqsave()
    }

    #[allow(dead_code)]
    fn set_hwirq_max(&self, hwirq_max: HardwareIrqNumber) {
        self.revmap_write_irqsave().hwirq_max = hwirq_max;
    }

    pub fn name(&self) -> Option<String> {
        if let Some(name) = self.name {
            return Some(name.to_string());
        }
        return self.allocated_name.lock_irqsave().clone();
    }

    pub fn set_name(&self, name: String) {
        *self.allocated_name.lock_irqsave() = Some(name);
    }

    /// The number of mapped interrupts
    #[allow(dead_code)]
    pub fn map_count(&self) -> u32 {
        self.revmap_read_irqsave().map.len() as u32
    }

    #[allow(dead_code)]
    pub fn host_data(&self) -> Option<Arc<dyn IrqChipData>> {
        self.inner.lock_irqsave().host_data.clone()
    }

    #[allow(dead_code)]
    pub fn set_host_data(&self, host_data: Option<Arc<dyn IrqChipData>>) {
        self.inner.lock_irqsave().host_data = host_data;
    }
}

/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#190
#[allow(dead_code)]
#[derive(Debug)]
struct IrqDomainRevMap {
    map: HashMap<HardwareIrqNumber, Arc<IrqData>>,
    hwirq_max: HardwareIrqNumber,
    irq_max: IrqNumber,
}

impl IrqDomainRevMap {
    fn insert(&mut self, hwirq: HardwareIrqNumber, irq_data: Arc<IrqData>) {
        self.map.insert(hwirq, irq_data);
    }

    #[allow(dead_code)]
    fn remove(&mut self, hwirq: HardwareIrqNumber) {
        self.map.remove(&hwirq);
    }

    #[allow(dead_code)]
    fn lookup(&self, hwirq: HardwareIrqNumber) -> Option<Arc<IrqData>> {
        self.map.get(&hwirq).cloned()
    }
}

bitflags! {
    pub struct IrqDomainFlags: u32 {
        /// Irq domain is hierarchical
        const HIERARCHY = (1 << 0);
        /// Irq domain name was allocated dynamically
        const NAME_ALLOCATED = (1 << 1);
        /// Irq domain is an IPI domain with virq per cpu
        const IPI_PER_CPU = (1 << 2);
        /// Irq domain is an IPI domain with single virq
        const IPI_SINGLE = (1 << 3);
        /// Irq domain implements MSIs
        const MSI = (1 << 4);
        /// Irq domain implements MSI remapping
        const MSI_REMAP = (1 << 5);
        /// Quirk to handle MSI implementations which do not provide masking
        const MSI_NOMASK_QUIRK = (1 << 6);
        /// Irq domain doesn't translate anything
        const NO_MAP = (1 << 7);
        /// Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved
        /// for implementation specific purposes and ignored by the core code
        const NONCORE = (1 << 16);
    }
}

/// 如果多个域有相同的设备节点，但服务于不同的目的（例如，一个域用于PCI/MSI，另一个用于有线IRQs），
/// 它们可以使用特定于总线的token进行区分。预计大多数域只会携带`DomainBusAny`。
///
/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#78
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IrqDomainBusToken {
    Any = 0,
    Wired,
    GenericMsi,
    PciMsi,
    PlatformMsi,
    Nexus,
    Ipi,
    FslMcMsi,
    TiSciIntaMsi,
    Wakeup,
    VmdMsi,
}

/// IrqDomain的操作方法
///
/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#107
pub trait IrqDomainOps: Debug + Send + Sync {
    /// 匹配一个中断控制器设备节点到一个主机。
    #[allow(dead_code)]
    fn match_node(
        &self,
        _irq_domain: &Arc<IrqDomain>,
        _device_node: &Arc<DeviceNode>,
        _bus_token: IrqDomainBusToken,
    ) -> bool {
        false
    }

    /// 创建或更新一个虚拟中断号与一个硬件中断号之间的映射。
    /// 对于给定的映射，这只会被调用一次。
    ///
    /// 如果没有实现这个方法，那么就会返回`ENOSYS`
    fn map(
        &self,
        _irq_domain: &Arc<IrqDomain>,
        _hwirq: HardwareIrqNumber,
        _virq: IrqNumber,
    ) -> Result<(), SystemError> {
        Err(SystemError::ENOSYS)
    }

    /// 删除一个虚拟中断号与一个硬件中断号之间的映射。
    #[allow(dead_code)]
    fn unmap(&self, irq_domain: &Arc<IrqDomain>, virq: IrqNumber);

    fn activate(
        &self,
        _domain: &Arc<IrqDomain>,
        _irq_data: &Arc<IrqData>,
        _reserve: bool,
    ) -> Result<(), SystemError> {
        Err(SystemError::ENOSYS)
    }

    fn deactivate(&self, _domain: &Arc<IrqDomain>, _irq_data: &Arc<IrqData>) {}
}

#[allow(dead_code)]
#[derive(Debug)]
pub struct IrqDomainChipGeneric {
    inner: SpinLock<InnerIrqDomainChipGeneric>,
}

#[allow(dead_code)]
#[derive(Debug)]
struct InnerIrqDomainChipGeneric {
    irqs_per_chip: u32,
    flags_to_clear: IrqGcFlags,
    flags_to_set: IrqGcFlags,
    gc_flags: IrqGcFlags,
    gc: Vec<Arc<IrqChipGeneric>>,
}
