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

use alloc::boxed::Box;
use async_trait::async_trait;
use device_api::{
    clock::Hertz,
    i2c::{I2CAddress, I2CDevice},
};
use yggdrasil_abi::{
    error::Error,
    io::device::i2c::{self, I2CRequestVariant},
    option::RequestValue,
    process::ProcessId,
};

use crate::{device::char::CharDevice, task::thread::Thread, vfs::FileReadiness};

// TODO timeouts and better access
#[async_trait]
impl CharDevice for I2CDevice {
    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 pid = Thread::current().process_id();
        self.check_lock(pid)?;
        self.i2c_read(self.slave_address(), buffer)
    }
    fn write_nonblocking(&self, buffer: &[u8]) -> Result<usize, Error> {
        let pid = Thread::current().process_id();
        self.check_lock(pid)?;
        self.i2c_write(self.slave_address(), buffer)
    }
    fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
        if let Ok(req) = I2CRequestVariant::try_from(option) {
            match req {
                I2CRequestVariant::SetAddress => {
                    let address = i2c::SetAddress::load_request(&buffer[..len])?;
                    let address = I2CAddress::try_from(address)?;
                    self.set_slave_address(address);
                    Ok(0)
                }
                I2CRequestVariant::SetSpeed => {
                    let speed = i2c::SetSpeed::load_request(&buffer[..len])?;
                    let speed = Hertz::from(speed);
                    let actual = self.set_speed(speed)?.0 as u32;
                    let len = i2c::SetSpeed::store_response(&actual, buffer)?;
                    Ok(len)
                }
                I2CRequestVariant::GetMasterInfo => {
                    let info = self.capabilities();
                    let len = i2c::GetMasterInfo::store_response(&info, buffer)?;
                    Ok(len)
                }
            }
        } else {
            Err(Error::InvalidArgument)
        }
    }

    fn open(&self, process: ProcessId) -> Result<(), Error> {
        I2CDevice::lock(self, process)
    }
    fn close(&self, process: ProcessId) -> Result<(), Error> {
        I2CDevice::release(self, process)
    }
}
impl FileReadiness for I2CDevice {
    fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
        let _ = cx;
        todo!()
    }
}
