use crate::hardware::error::WrappingError;
use anyhow::{Result, ensure};
use embedded_hal::spi::SpiDevice;
use log::trace;
use std::any::type_name;
use std::fmt::{Debug, Formatter};

pub struct Mcp3208<SPI> {
    spi: SPI,
    vref: f64,
}

impl<SPI> Debug for Mcp3208<SPI> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Mcp3208<SPI={}> {{ vref: {} }}",
            type_name::<SPI>(),
            self.vref
        )
    }
}

impl<SPI> Mcp3208<SPI>
where
    SPI: SpiDevice<u8>,
    SPI::Error: Send,
    SPI::Error: Sync,
    SPI::Error: 'static,
{
    pub fn new(spi: SPI, vref: f64) -> Self {
        trace!(
            "Mcp3208<SPI={}>::new(spi, vref: {vref})",
            type_name::<SPI>()
        );
        Self { spi, vref }
    }

    pub fn read_single(&mut self, channel: u8) -> Result<f64> {
        trace!("Mcp3208::read_single(self: {self:?}, channel: {channel})");
        ensure!(channel < 8, "Invalid Channel {channel}");

        // We don't care what the third byte written is
        let mut write_bits = [0b00000110, 0b00000000, 0b00000000];
        write_bits[0] |= (channel.unbounded_shr(2)) & 0xFF;
        write_bits[1] |= (channel.unbounded_shl(6)) & 0xFF;

        let mut read_bits = [0u8; 3];
        self.spi
            .transfer(&mut read_bits, &write_bits)
            .map_err(WrappingError)?;

        let value: u16 = u16::from_be_bytes([read_bits[1], read_bits[2]]) & 0x0FFF;

        Ok(((value as f64) / (0xFFF as f64)) * self.vref)
    }
}
