use core::{
    fmt,
    ops::Deref,
    sync::atomic::{AtomicU16, AtomicU32, Ordering},
};

use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use yggdrasil_abi::{error::Error, io::device::i2c::I2CMasterInfo, process::ProcessId};

use crate::{
    clock::Hertz,
    device::{Device, DeviceInitContext},
};

pub struct I2CAddress(u16);

pub struct I2CDevice {
    device: Arc<dyn I2CController>,
    address: AtomicU16,
    user: AtomicU32,
}

pub enum I2CMessage<'a> {
    Read(&'a mut [u8]),
    Write(&'a [u8]),
}

pub struct I2CTransfer<'a> {
    pub repeat_start: bool,
    pub messages: &'a mut [I2CMessage<'a>],
}

#[async_trait]
pub trait I2CController: Device {
    fn bus_number(&self) -> u32;
    fn child_number(&self) -> Option<u32>;

    fn set_speed(&self, speed: Hertz) -> Result<Hertz, Error>;
    fn capabilities(&self) -> I2CMasterInfo;

    async fn i2c_transfer(
        &self,
        address: I2CAddress,
        transfer: &mut I2CTransfer,
    ) -> Result<usize, Error>;
    // async fn i2c_write(&self, address: I2CAddress, buffer: &[u8]) -> Result<usize, Error>;
    // async fn i2c_read(&self, address: I2CAddress, buffer: &mut [u8]) -> Result<usize, Error>;
}

impl I2CAddress {
    pub fn as_8_bit(&self) -> Option<u8> {
        self.0.try_into().ok()
    }

    pub fn as_10_bit(&self) -> u16 {
        self.0
    }

    pub fn is_8_bit(&self) -> bool {
        self.0 <= 0xFF
    }
}

impl fmt::Display for I2CAddress {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

impl From<u8> for I2CAddress {
    fn from(value: u8) -> Self {
        Self(value as u16)
    }
}

impl TryFrom<u16> for I2CAddress {
    type Error = Error;

    fn try_from(value: u16) -> Result<Self, Self::Error> {
        if value >= (1 << 10) {
            return Err(Error::InvalidArgument);
        }
        Ok(Self(value))
    }
}

impl Deref for I2CDevice {
    type Target = dyn I2CController;

    fn deref(&self) -> &Self::Target {
        &*self.device
    }
}

impl From<Arc<dyn I2CController>> for I2CDevice {
    fn from(value: Arc<dyn I2CController>) -> Self {
        Self {
            device: value,
            address: AtomicU16::new(0),
            user: AtomicU32::new(0),
        }
    }
}

impl I2CDevice {
    pub fn set_slave_address(&self, address: I2CAddress) {
        self.address.store(address.0, Ordering::Release);
    }

    pub fn set_speed(&self, speed: Hertz) -> Result<Hertz, Error> {
        self.device.set_speed(speed)
    }

    pub fn slave_address(&self) -> I2CAddress {
        I2CAddress(self.address.load(Ordering::Acquire))
    }

    pub fn check_lock(&self, id: ProcessId) -> Result<(), Error> {
        if self.user.load(Ordering::Acquire) == id.bits() {
            Ok(())
        } else {
            Err(Error::PermissionDenied)
        }
    }

    pub fn lock(&self, process: ProcessId) -> Result<(), Error> {
        if self
            .user
            .compare_exchange(0, process.bits(), Ordering::Acquire, Ordering::Relaxed)
            .is_ok()
        {
            Ok(())
        } else {
            Err(Error::WouldBlock)
        }
    }

    pub fn release(&self, process: ProcessId) -> Result<(), Error> {
        if self
            .user
            .compare_exchange(process.bits(), 0, Ordering::Release, Ordering::Relaxed)
            .is_ok()
        {
            Ok(())
        } else {
            Err(Error::InvalidOperation)
        }
    }
}

impl Device for I2CDevice {
    unsafe fn init(self: Arc<Self>, cx: DeviceInitContext) -> Result<(), Error> {
        unsafe { self.device.clone().init(cx) }
    }

    unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
        unsafe { self.device.clone().init_irq() }
    }

    fn display_name(&self) -> &str {
        self.device.display_name()
    }
}
