#![no_std]

extern crate alloc;

use core::task::{Context, Poll};

use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use device_api::device::Device;
use libk::{device::char::CharDevice, vfs::FileReadiness};
use libk_util::{OneTimeInit, ring::LossyRingQueue};
use yggdrasil_abi::{
    error::Error,
    io::{KeyboardKeyEvent, MouseEvent},
};

#[derive(Clone, Copy)]
pub struct KeyboardDevice;
#[derive(Clone, Copy)]
pub struct MouseDevice;

impl FileReadiness for KeyboardDevice {
    fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
        KEYBOARD_INPUT_QUEUE.poll_readable(cx).map(Ok)
    }
}

impl Device for KeyboardDevice {
    fn display_name(&self) -> &str {
        "Keyboard input pseudo-device"
    }
}

#[async_trait]
impl CharDevice for KeyboardDevice {
    async fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {
        if buf.len() < 4 {
            return Ok(0);
        }

        let ev = KEYBOARD_INPUT_QUEUE.read().await;

        buf[..4].copy_from_slice(&ev.as_bytes());

        Ok(4)
    }

    fn read_nonblocking(&self, buf: &mut [u8]) -> Result<usize, Error> {
        if buf.len() < 4 {
            return Ok(0);
        }

        let ev = KEYBOARD_INPUT_QUEUE.try_read().ok_or(Error::WouldBlock)?;

        buf[..4].copy_from_slice(&ev.as_bytes());

        Ok(4)
    }

    fn is_writeable(&self) -> bool {
        false
    }

    fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
        let _ = option;
        let _ = buffer;
        let _ = len;
        Err(Error::InvalidOperation)
    }

    fn is_terminal(&self) -> bool {
        false
    }
}

impl MouseDevice {
    fn write_report(buf: &mut [u8], report: &MouseEvent) -> Result<usize, Error> {
        buf[0] = report.dx as u8;
        buf[1] = report.dy as u8;
        buf[2] = report.buttons;
        buf[3] = 0;

        Ok(4)
    }
}

impl FileReadiness for MouseDevice {
    fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
        MOUSE_INPUT_QUEUE.poll_readable(cx).map(Ok)
    }
}

impl Device for MouseDevice {
    fn display_name(&self) -> &str {
        "Mouse input pseudo-device"
    }
}

#[async_trait]
impl CharDevice for MouseDevice {
    async fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {
        if buf.len() < 4 {
            return Ok(0);
        }

        let ev = MOUSE_INPUT_QUEUE.read().await;
        Self::write_report(buf, &ev)
    }

    fn read_nonblocking(&self, buf: &mut [u8]) -> Result<usize, Error> {
        if buf.len() < 4 {
            return Ok(0);
        }

        let ev = MOUSE_INPUT_QUEUE.try_read().ok_or(Error::WouldBlock)?;
        Self::write_report(buf, &ev)
    }

    fn is_writeable(&self) -> bool {
        false
    }

    fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
        let _ = option;
        let _ = buffer;
        let _ = len;
        Err(Error::InvalidOperation)
    }

    fn is_terminal(&self) -> bool {
        false
    }
}

static KEYBOARD_INPUT_QUEUE: LossyRingQueue<KeyboardKeyEvent> = LossyRingQueue::with_capacity(32);
static MOUSE_INPUT_QUEUE: LossyRingQueue<MouseEvent> = LossyRingQueue::with_capacity(64);
static KEYBOARD_DEVICE: OneTimeInit<Arc<KeyboardDevice>> = OneTimeInit::new();
static MOUSE_DEVICE: OneTimeInit<Arc<MouseDevice>> = OneTimeInit::new();

pub fn setup_keyboard() -> Arc<KeyboardDevice> {
    KEYBOARD_DEVICE
        .or_init_with(|| Arc::new(KeyboardDevice))
        .clone()
}

pub fn setup_mouse() -> Arc<MouseDevice> {
    MOUSE_DEVICE.or_init_with(|| Arc::new(MouseDevice)).clone()
}

pub fn send_mouse_event(ev: MouseEvent) {
    MOUSE_INPUT_QUEUE.write(ev);
}

pub fn send_keyboard_event(ev: KeyboardKeyEvent) {
    KEYBOARD_INPUT_QUEUE.write(ev);
}
