use bitvec::{array::BitArray, field::BitField};
use core::{
    convert::{TryFrom, TryInto},
    marker::PhantomData,
    ops::{Deref, DerefMut},
};
use funty::Integral;
use crate::{ClearCapability, ReadCapability, WriteCapability};
pub trait AddressableDevice {
    type AddressType;
}
pub trait RegisterDevice: AddressableDevice {
    type Error;
    fn write_register<const SIZE_BYTES: usize>(
        &mut self,
        address: Self::AddressType,
        data: &BitArray<[u8; SIZE_BYTES]>,
    ) -> Result<(), Self::Error>;
    fn read_register<const SIZE_BYTES: usize>(
        &mut self,
        address: Self::AddressType,
        data: &mut BitArray<[u8; SIZE_BYTES]>,
    ) -> Result<(), Self::Error>;
}
pub trait AsyncRegisterDevice: AddressableDevice {
    type Error;
    async fn write_register<const SIZE_BYTES: usize>(
        &mut self,
        address: Self::AddressType,
        data: &BitArray<[u8; SIZE_BYTES]>,
    ) -> Result<(), Self::Error>;
    async fn read_register<const SIZE_BYTES: usize>(
        &mut self,
        address: Self::AddressType,
        data: &mut BitArray<[u8; SIZE_BYTES]>,
    ) -> Result<(), Self::Error>;
}
pub trait Register<const SIZE_BYTES: usize> {
    const ZERO: Self;
    type AddressType;
    const ADDRESS: Self::AddressType;
    type RWType;
    const SIZE_BITS: usize;
    type WriteFields: From<Self> + Into<Self> + Deref<Target = Self> + DerefMut
    where
        Self: Sized;
    type ReadFields: From<Self> + Into<Self> + Deref<Target = Self> + DerefMut
    where
        Self: Sized;
    const LE: bool;
    fn bits_mut(&mut self) -> &mut BitArray<[u8; SIZE_BYTES]>;
    fn bits(&self) -> &BitArray<[u8; SIZE_BYTES]>;
    fn reset_value() -> Self
    where
        Self: Sized,
    {
        Self::ZERO
    }
}
pub struct RegisterOperation<'a, D, R, const SIZE_BYTES: usize>
where
    R: Register<SIZE_BYTES>,
{
    device: &'a mut D,
    _phantom: PhantomData<R>,
}
impl<'a, D, R, const SIZE_BYTES: usize> RegisterOperation<'a, D, R, SIZE_BYTES>
where
    R: Register<SIZE_BYTES>,
{
    #[doc(hidden)]
    pub fn new(device: &'a mut D) -> Self {
        Self {
            device,
            _phantom: PhantomData,
        }
    }
}
impl<'a, D, R, const SIZE_BYTES: usize> RegisterOperation<'a, D, R, SIZE_BYTES>
where
    D: RegisterDevice<AddressType = R::AddressType>,
    R: Register<SIZE_BYTES>,
    R::RWType: WriteCapability,
{
    pub fn write(
        &mut self,
        f: impl FnOnce(&mut R::WriteFields) -> &mut R::WriteFields,
    ) -> Result<(), D::Error> {
        let mut register = R::reset_value().into();
        f(&mut register);
        self.device
            .write_register::<SIZE_BYTES>(R::ADDRESS, register.bits())
    }
    pub fn write_with_zero(
        &mut self,
        f: impl FnOnce(&mut R::WriteFields) -> &mut R::WriteFields,
    ) -> Result<(), D::Error> {
        let mut register = R::ZERO.into();
        f(&mut register);
        self.device
            .write_register::<SIZE_BYTES>(R::ADDRESS, register.bits())
    }
}
impl<'a, D, R, const SIZE_BYTES: usize> RegisterOperation<'a, D, R, SIZE_BYTES>
where
    D: RegisterDevice<AddressType = R::AddressType>,
    R: Register<SIZE_BYTES>,
    R::RWType: ReadCapability,
{
    pub fn read(&mut self) -> Result<R::ReadFields, D::Error> {
        let mut register = R::ZERO;
        self.device
            .read_register::<SIZE_BYTES>(R::ADDRESS, register.bits_mut())?;
        Ok(register.into())
    }
}
impl<'a, D, R, const SIZE_BYTES: usize> RegisterOperation<'a, D, R, SIZE_BYTES>
where
    D: RegisterDevice<AddressType = R::AddressType>,
    R: Register<SIZE_BYTES>,
    R::RWType: ReadCapability + WriteCapability,
{
    pub fn modify(
        &mut self,
        f: impl FnOnce(&mut R::WriteFields) -> &mut R::WriteFields,
    ) -> Result<(), D::Error> {
        let mut register = self.read()?.into().into();
        f(&mut register);
        self.device
            .write_register::<SIZE_BYTES>(R::ADDRESS, register.into().bits())
    }
}
impl<'a, D, R, const SIZE_BYTES: usize> RegisterOperation<'a, D, R, SIZE_BYTES>
where
    D: RegisterDevice<AddressType = R::AddressType>,
    R: Register<SIZE_BYTES>,
    R::RWType: ClearCapability,
{
    pub fn clear(&mut self) -> Result<(), D::Error> {
        self.device
            .write_register::<SIZE_BYTES>(R::ADDRESS, R::reset_value().bits())
    }
}
impl<'a, D, R, const SIZE_BYTES: usize> RegisterOperation<'a, D, R, SIZE_BYTES>
where
    D: AsyncRegisterDevice<AddressType = R::AddressType>,
    R: Register<SIZE_BYTES>,
    R::RWType: WriteCapability,
{
    pub async fn write_async(
        &mut self,
        f: impl FnOnce(&mut R::WriteFields) -> &mut R::WriteFields,
    ) -> Result<(), D::Error> {
        let mut register = R::reset_value().into();
        f(&mut register);
        self.device
            .write_register::<SIZE_BYTES>(R::ADDRESS, register.bits())
            .await
    }
    pub async fn write_with_zero_async(
        &mut self,
        f: impl FnOnce(&mut R::WriteFields) -> &mut R::WriteFields,
    ) -> Result<(), D::Error> {
        let mut register = R::ZERO.into();
        f(&mut register);
        self.device
            .write_register::<SIZE_BYTES>(R::ADDRESS, register.bits())
            .await
    }
}
impl<'a, D, R, const SIZE_BYTES: usize> RegisterOperation<'a, D, R, SIZE_BYTES>
where
    D: AsyncRegisterDevice<AddressType = R::AddressType>,
    R: Register<SIZE_BYTES>,
    R::RWType: ReadCapability,
{
    pub async fn read_async(&mut self) -> Result<R::ReadFields, D::Error> {
        let mut register = R::ZERO;
        self.device
            .read_register::<SIZE_BYTES>(R::ADDRESS, register.bits_mut())
            .await?;
        Ok(register.into())
    }
}
impl<'a, D, R, const SIZE_BYTES: usize> RegisterOperation<'a, D, R, SIZE_BYTES>
where
    D: AsyncRegisterDevice<AddressType = R::AddressType>,
    R: Register<SIZE_BYTES>,
    R::RWType: ReadCapability + WriteCapability,
{
    pub async fn modify_async(
        &mut self,
        f: impl FnOnce(&mut R::WriteFields) -> &mut R::WriteFields,
    ) -> Result<(), D::Error> {
        let mut register = self.read_async().await?.into().into();
        f(&mut register);
        self.device
            .write_register::<SIZE_BYTES>(R::ADDRESS, register.into().bits())
            .await
    }
}
impl<'a, D, R, const SIZE_BYTES: usize> RegisterOperation<'a, D, R, SIZE_BYTES>
where
    D: AsyncRegisterDevice<AddressType = R::AddressType>,
    R: Register<SIZE_BYTES>,
    R::RWType: ClearCapability,
{
    pub async fn clear_async(&mut self) -> Result<(), D::Error> {
        self.device
            .write_register::<SIZE_BYTES>(R::ADDRESS, R::reset_value().bits())
            .await
    }
}
#[doc(hidden)]
pub fn read_field<
    RD,
    R,
    DATA,
    BACKING,
    const START: usize,
    const END: usize,
    const SIZE_BYTES: usize,
>(
    register: &R,
) -> Result<DATA, <BACKING as TryInto<DATA>>::Error>
where
    RD: Deref<Target = R>,
    R: Register<SIZE_BYTES>,
    DATA: TryFrom<BACKING> + Into<BACKING>,
    BACKING: Integral,
{
    if R::LE {
        register.bits()[START..END].load_le::<BACKING>().try_into()
    } else {
        register.bits()[START..END].load_be::<BACKING>().try_into()
    }
}
#[doc(hidden)]
pub fn read_field_strict<
    RD,
    R,
    DATA,
    BACKING,
    const START: usize,
    const END: usize,
    const SIZE_BYTES: usize,
>(
    register: &R,
) -> DATA
where
    RD: Deref<Target = R>,
    R: Register<SIZE_BYTES>,
    DATA: From<BACKING> + Into<BACKING>,
    BACKING: Integral,
{
    if R::LE {
        register.bits()[START..END].load_le::<BACKING>().into()
    } else {
        register.bits()[START..END].load_be::<BACKING>().into()
    }
}
#[doc(hidden)]
pub fn read_field_no_convert<
    RD,
    R,
    BACKING,
    const START: usize,
    const END: usize,
    const SIZE_BYTES: usize,
>(
    register: &R,
) -> BACKING
where
    RD: Deref<Target = R>,
    R: Register<SIZE_BYTES>,
    BACKING: Integral,
{
    if R::LE {
        register.bits()[START..END].load_le::<BACKING>()
    } else {
        register.bits()[START..END].load_be::<BACKING>()
    }
}
#[doc(hidden)]
pub fn read_field_bool<RD, R, DATA, const START: usize, const SIZE_BYTES: usize>(
    register: &R,
) -> Result<DATA, <bool as TryInto<DATA>>::Error>
where
    RD: Deref<Target = R>,
    R: Register<SIZE_BYTES>,
    DATA: TryFrom<bool> + Into<bool>,
{
    register.bits()[START].try_into()
}
#[doc(hidden)]
pub fn read_field_bool_strict<RD, R, DATA, const START: usize, const SIZE_BYTES: usize>(
    register: &R,
) -> DATA
where
    RD: Deref<Target = R>,
    R: Register<SIZE_BYTES>,
    DATA: From<bool> + Into<bool>,
{
    register.bits()[START].into()
}
#[doc(hidden)]
pub fn read_field_bool_no_convert<RD, R, const START: usize, const SIZE_BYTES: usize>(
    register: &R,
) -> bool
where
    RD: Deref<Target = R>,
    R: Register<SIZE_BYTES>,
{
    register.bits()[START]
}
#[doc(hidden)]
pub fn write_field<
    RD,
    R,
    DATA,
    BACKING,
    const START: usize,
    const END: usize,
    const SIZE_BYTES: usize,
>(
    register: &mut RD,
    data: DATA,
) -> &mut RD
where
    RD: DerefMut<Target = R>,
    R: Register<SIZE_BYTES>,
    DATA: TryFrom<BACKING> + Into<BACKING>,
    BACKING: Integral,
{
    if R::LE {
        register.bits_mut()[START..END].store_le(data.into());
    } else {
        register.bits_mut()[START..END].store_be(data.into());
    }
    register
}
#[doc(hidden)]
pub fn write_field_no_convert<
    RD,
    R,
    BACKING,
    const START: usize,
    const END: usize,
    const SIZE_BYTES: usize,
>(
    register: &mut RD,
    data: BACKING,
) -> &mut RD
where
    RD: DerefMut<Target = R>,
    R: Register<SIZE_BYTES>,
    BACKING: Integral,
{
    if R::LE {
        register.bits_mut()[START..END].store_le(data);
    } else {
        register.bits_mut()[START..END].store_be(data);
    }
    register
}
#[doc(hidden)]
pub fn write_field_bool<RD, R, DATA, const START: usize, const SIZE_BYTES: usize>(
    register: &mut RD,
    data: DATA,
) -> &mut RD
where
    RD: DerefMut<Target = R>,
    R: Register<SIZE_BYTES>,
    DATA: TryFrom<bool> + Into<bool>,
{
    register.bits_mut().set(START, data.into());
    register
}
#[doc(hidden)]
pub fn write_field_bool_no_convert<RD, R, const START: usize, const SIZE_BYTES: usize>(
    register: &mut RD,
    data: bool,
) -> &mut RD
where
    RD: DerefMut<Target = R>,
    R: Register<SIZE_BYTES>,
{
    register.bits_mut().set(START, data);
    register
}