use core::{any::Any, ops::Deref};

use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use device_api::device::Device;
use yggdrasil_abi::error::Error;

use crate::vfs::{CommonImpl, FileReadiness, NodeRef};

#[async_trait]
pub trait CharDevice: Device + FileReadiness {
    async fn read(&self, buffer: &mut [u8]) -> Result<usize, Error> {
        self.read_nonblocking(buffer)
    }
    async fn write(&self, buffer: &[u8]) -> Result<usize, Error> {
        self.write_nonblocking(buffer)
    }

    fn read_nonblocking(&self, buffer: &mut [u8]) -> Result<usize, Error> {
        let _ = buffer;
        Err(Error::NotImplemented)
    }
    fn write_nonblocking(&self, buffer: &[u8]) -> Result<usize, Error> {
        let _ = buffer;
        Err(Error::NotImplemented)
    }

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

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

#[derive(Clone)]
pub struct CharDeviceFile(pub(crate) Arc<dyn CharDevice>);

impl CommonImpl for CharDeviceFile {
    fn size(&self, node: &NodeRef) -> Result<u64, Error> {
        let _ = node;
        Ok(0)
    }

    fn as_any(&self) -> &dyn Any {
        self as _
    }
}

impl Deref for CharDeviceFile {
    type Target = dyn CharDevice;

    fn deref(&self) -> &Self::Target {
        self.0.as_ref()
    }
}
