//! Intel 8042 PS/2 controller driver implemenation
use abi::{
    error::Error,
    io::{KeyboardKey, KeyboardKeyEvent},
};
use alloc::sync::Arc;
use device_api::{
    device::{Device, DeviceInitContext},
    interrupt::{InterruptHandler, Irq, IrqVector},
};
use kernel_arch_x86::{
    intrinsics::{IoPort, IoPortAccess},
    ISA_IRQ_OFFSET,
};
use libk::device::external_interrupt_controller;
use libk_util::sync::IrqSafeSpinlock;

use codeset::{CODE_SET_1_00, CODE_SET_1_E0};

mod codeset;

struct Inner {
    command: IoPort<u8>,
    data: IoPort<u8>,

    e0: bool,
}

/// PS/2 controller driver
pub struct PS2Controller {
    primary_irq: Irq,
    #[allow(unused)]
    auxiliary_irq: Irq,

    inner: IrqSafeSpinlock<Inner>,
}

fn translate(e0: bool, key: u8) -> KeyboardKey {
    debug_assert!(key < 0x80);

    if e0 {
        CODE_SET_1_E0[key as usize]
    } else {
        CODE_SET_1_00[key as usize]
    }
}

impl Inner {
    const STATUS_OUTPUT_FULL: u8 = 1 << 0;
    const STATUS_INPUT_FULL: u8 = 1 << 1;

    fn send_command(&mut self, cmd: u8) {
        while self.command.read() & Self::STATUS_INPUT_FULL != 0 {
            core::hint::spin_loop();
        }
        self.command.write(cmd);
    }

    // fn recv_timeout(&mut self, timeout: u64) -> Option<u8> {
    //     let mut counter = 0;
    //     while self.command.read() & Self::STATUS_OUTPUT_FULL == 0 {
    //         counter += 1;
    //         if counter > timeout {
    //             return None;
    //         }
    //         core::hint::spin_loop();
    //     }
    //     Some(self.data.read())
    // }

    fn try_recv(&mut self) -> Option<u8> {
        if self.command.read() & Inner::STATUS_OUTPUT_FULL != 0 {
            Some(self.data.read())
        } else {
            None
        }
    }
}

impl InterruptHandler for PS2Controller {
    fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
        let mut count = 0;
        let mut inner = self.inner.lock();

        loop {
            let Some(mut scancode) = inner.try_recv() else {
                break;
            };

            count += 1;

            if scancode == 0xE0 {
                inner.e0 = true;
                continue;
            }
            let release = scancode >= 0x80;

            if release {
                scancode -= 0x80;
            }

            let key = translate(inner.e0, scancode);
            let event = if release {
                KeyboardKeyEvent::Released(key)
            } else {
                KeyboardKeyEvent::Pressed(key)
            };

            inner.e0 = false;

            ygg_driver_input::send_event(event);
        }

        count != 0
    }
}

impl Device for PS2Controller {
    fn display_name(&self) -> &'static str {
        "PS/2 Controller"
    }

    unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
        Ok(())
    }

    unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
        let intc = external_interrupt_controller()?;

        let mut inner = self.inner.lock();
        // let intc = PLATFORM.interrupt_controller();

        intc.register_irq(self.primary_irq, Default::default(), self.clone())?;

        // Disable PS/2 devices from sending any further data
        inner.send_command(0xAD);
        inner.send_command(0xA7);

        // Flush the buffer
        while inner.command.read() & Inner::STATUS_OUTPUT_FULL != 0 {
            inner.data.read();
        }

        // Enable primary port
        inner.send_command(0xAE);

        intc.enable_irq(self.primary_irq)?;
        intc.enable_irq(self.auxiliary_irq)?;

        Ok(())
    }
}

impl PS2Controller {
    /// Constructs a new instance of the device
    const fn new(primary_irq: Irq, auxiliary_irq: Irq, cmd_port: u16, data_port: u16) -> Self {
        let inner = Inner {
            command: IoPort::new(cmd_port),
            data: IoPort::new(data_port),
            e0: false,
        };

        Self {
            primary_irq,
            auxiliary_irq,
            inner: IrqSafeSpinlock::new(inner),
        }
    }

    pub fn setup() -> Result<Arc<Self>, Error> {
        let this = Arc::new(PS2Controller::new(
            Irq::External(ISA_IRQ_OFFSET + 1),
            Irq::External(ISA_IRQ_OFFSET + 12),
            0x64,
            0x60,
        ));
        unsafe { this.clone().init_irq() }?;
        Ok(this)
    }
}

// pub static PS2: PS2Controller = PS2Controller::new(
//     Irq::External(ISA_IRQ_OFFSET + 1),
//     Irq::External(ISA_IRQ_OFFSET + 12),
//     0x64,
//     0x60,
// );
