use alloc::sync::Arc;
use device_api::{
    clock::Hertz,
    interrupt::{InterruptHandler, Irq},
};
use kernel_arch_x86::intrinsics;
use libk::{device::external_interrupt_controller, error::Error};

use crate::{Access, Config, PortConfig};

pub struct Io8250Access {
    io_base: u16,
}

pub struct Io8250Config {
    ports: [Io8250PortConfig; 2],
    irq: Irq,
}

pub struct Io8250PortConfig {
    io_base: u16,
}

impl Io8250Config {
    pub fn new(port_a: u16, port_b: u16, irq: Irq) -> Self {
        Self {
            ports: [
                Io8250PortConfig { io_base: port_a },
                Io8250PortConfig { io_base: port_b },
            ],
            irq,
        }
    }
}

impl Access for Io8250Access {
    type Config = Io8250Config;

    unsafe fn map(config: &Io8250PortConfig) -> Result<Self, Error> {
        Ok(Self {
            io_base: config.io_base,
        })
    }

    fn read(&self, reg: u8) -> u8 {
        unsafe { intrinsics::inb(self.io_base + reg as u16) }
    }

    fn write(&mut self, reg: u8, val: u8) {
        unsafe { intrinsics::outb(self.io_base + reg as u16, val) };
    }
}

impl Config for Io8250Config {
    type Port = Io8250PortConfig;

    fn ports(&self) -> &[Self::Port] {
        &self.ports
    }

    fn enable_interrupt(&self, handler: Arc<dyn InterruptHandler>) -> Result<(), Error> {
        let intc = external_interrupt_controller()?;
        intc.register_irq(self.irq, Default::default(), handler)?;
        intc.enable_irq(self.irq)?;
        Ok(())
    }
}

impl PortConfig for Io8250PortConfig {
    fn input_clock_frequency(&self) -> Result<Hertz, Error> {
        Ok(Hertz(115200 * 16))
    }

    fn enable_clocks(&self) -> Result<(), Error> {
        Ok(())
    }

    fn deassert_resets(&self) -> Result<(), Error> {
        Ok(())
    }
}
