use core::{ptr::NonNull, time::Duration};

use acpi::PhysicalMapping;
use acpi_system::AcpiSystemError;
use alloc::sync::Arc;
use device_api::{
    device::Device,
    interrupt::{InterruptHandler, Irq, IrqVector},
};
use kernel_arch_x86::{intrinsics, ISA_IRQ_OFFSET};
use libk::device::external_interrupt_controller;
use libk_mm::{
    address::{PhysicalAddress, Virtualize},
    pointer::PhysicalRef,
};

use crate::{
    mem::{read_memory, write_memory},
    ACPI_SYSTEM,
};

#[derive(Clone, Copy)]
#[doc(hidden)]
pub struct AcpiHandlerImpl;

struct SciHandler;

impl acpi_system::Handler for AcpiHandlerImpl {
    type MappedSlice = PhysicalRef<'static, [u8]>;

    unsafe fn map_slice(address: u64, length: u64) -> Self::MappedSlice {
        unsafe {
            PhysicalRef::map_slice(
                PhysicalAddress::from_u64(address),
                length.try_into().unwrap(),
            )
        }
    }

    fn io_read_u8(port: u16) -> u8 {
        let value = unsafe { intrinsics::inb(port) };
        log::trace!("io_read_u8 {:#x} <- {:#x}", port, value);
        value
    }

    fn io_read_u16(port: u16) -> u16 {
        let value = unsafe { intrinsics::inw(port) };
        log::trace!("io_read_u16 {:#x} <- {:#x}", port, value);
        value
    }

    fn io_read_u32(port: u16) -> u32 {
        let value = unsafe { intrinsics::inl(port) };
        log::trace!("io_read_u32 {:#x} <- {:#x}", port, value);
        value
    }

    fn io_write_u8(port: u16, value: u8) {
        log::trace!("io_write_u8 {:#x}, {:#x}", port, value);
        unsafe { intrinsics::outb(port, value) }
    }

    fn io_write_u16(port: u16, value: u16) {
        log::trace!("io_write_u16 {:#x}, {:#x}", port, value);
        unsafe { intrinsics::outw(port, value) }
    }

    fn io_write_u32(port: u16, value: u32) {
        log::trace!("io_write_u32 {:#x}, {:#x}", port, value);
        unsafe { intrinsics::outl(port, value) }
    }

    fn mem_read_u8(address: u64) -> u8 {
        let value = unsafe { read_memory(PhysicalAddress::from_u64(address)) };
        log::trace!("mem_read_u8 {:#x} -> {:#x}", address, value);
        value
    }

    fn mem_read_u16(address: u64) -> u16 {
        let value = unsafe { read_memory(PhysicalAddress::from_u64(address)) };
        log::trace!("mem_read_u16 {:#x} -> {:#x}", address, value);
        value
    }

    fn mem_read_u32(address: u64) -> u32 {
        let value = unsafe { read_memory(PhysicalAddress::from_u64(address)) };
        log::trace!("mem_read_u32 {:#x} -> {:#x}", address, value);
        value
    }

    fn mem_read_u64(address: u64) -> u64 {
        let value = unsafe { read_memory(PhysicalAddress::from_u64(address)) };
        log::trace!("mem_read_u64 {:#x} -> {:#x}", address, value);
        value
    }

    fn mem_write_u8(address: u64, value: u8) {
        log::trace!("mem_write_u8 {:#x}, {:#x}", address, value);
        unsafe { write_memory(PhysicalAddress::from_u64(address), value) }
    }

    fn mem_write_u16(address: u64, value: u16) {
        log::trace!("mem_write_u16 {:#x}, {:#x}", address, value);
        unsafe { write_memory(PhysicalAddress::from_u64(address), value) }
    }

    fn mem_write_u32(address: u64, value: u32) {
        log::trace!("mem_write_u32 {:#x}, {:#x}", address, value);
        unsafe { write_memory(PhysicalAddress::from_u64(address), value) }
    }

    fn mem_write_u64(address: u64, value: u64) {
        log::trace!("mem_write_u64 {:#x}, {:#x}", address, value);
        unsafe { write_memory(PhysicalAddress::from_u64(address), value) }
    }

    fn install_interrupt_handler(irq: u32) -> Result<(), AcpiSystemError> {
        log::info!("Installing ACPI SCI handler at IRQ #{}", irq);

        let intc = external_interrupt_controller().expect("No external intc");
        let handler = Arc::new(SciHandler);
        let irq = Irq::External(irq + ISA_IRQ_OFFSET);

        intc.register_irq(irq, Default::default(), handler).unwrap();
        intc.enable_irq(irq).unwrap();

        Ok(())
    }

    fn stall(_duration: Duration) {
        // TODO polling_sleep is not yet implemented properly
        todo!()
        // util::polling_sleep(duration).ok();
    }
}

impl rsdp::handler::AcpiHandler for AcpiHandlerImpl {
    unsafe fn map_physical_region<T>(
        &self,
        physical_address: usize,
        size: usize,
    ) -> PhysicalMapping<Self, T> {
        unsafe {
            PhysicalMapping::new(
                physical_address,
                NonNull::new_unchecked(
                    PhysicalAddress::from_usize(physical_address).virtualize() as *mut T
                ),
                size,
                size,
                *self,
            )
        }
    }

    fn unmap_physical_region<T>(_region: &acpi::PhysicalMapping<Self, T>) {}
}

impl InterruptHandler for SciHandler {
    fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
        log::trace!("ACPI SCI received");
        ACPI_SYSTEM.get().lock().handle_sci();
        true
    }
}

impl Device for SciHandler {
    fn display_name(&self) -> &str {
        "ACPI SCI handler"
    }
}
