#![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::{ring::LossyRingQueue, OneTimeInit};
use yggdrasil_abi::{error::Error, io::KeyboardKeyEvent};

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

impl FileReadiness for KeyboardDevice {
    fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
        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 = 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 = 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
    }
}

static INPUT_QUEUE: LossyRingQueue<KeyboardKeyEvent> = LossyRingQueue::with_capacity(32);
static KEYBOARD_DEVICE: OneTimeInit<Arc<KeyboardDevice>> = OneTimeInit::new();

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

pub fn send_event(ev: KeyboardKeyEvent) {
    INPUT_QUEUE.write(ev);
}
