Documentation
mod bmi270;

pub use bmi270::*;
pub use embedded_hal::blocking::spi::*;
pub use linux_embedded_hal::spidev::{SpiModeFlags, SpidevOptions};
pub use linux_embedded_hal::Spidev;

use std::time::Duration;

pub struct Bmi270<C> {
    communication: C,
    acc_range: f64,
    gyr_range: f64,
}

impl Bmi270Write for Bmi270<Spidev> {
    fn bmi270_write(&mut self, addr: u8, value: &[u8]) {
        let mut words = vec![addr];
        words.extend_from_slice(value);
        self.communication.write(&words).unwrap();
    }
}

impl Bmi270ReadWrite for Bmi270<Spidev> {
    fn bmi270_read_write(&mut self, addr: u8, len: usize) -> Vec<u8> {
        let mut tx_buff = vec![addr | 0x80];
        tx_buff.extend_from_slice(&vec![0xff; len + 1]);
        self.communication.transfer(&mut tx_buff).unwrap();
        tx_buff[2..].to_vec()
    }
}

impl Bmi270<Spidev> {
    pub fn new(spi: Spidev, config: Bmi270Config) -> Self {
        let mut imu = Self {
            communication: spi,
            acc_range: 0.0,
            gyr_range: 0.0,
        };
        // 检测陀螺仪连接
        while imu.bmi270_read_write(ADDR_CHIP_ID, 1)[0] != 0x24 {
            println!("等待陀螺仪连接,检查陀螺仪连线");
        }

        println!("已检测到陀螺仪,尝试初始化");
        imu.bmi270_write(ADDR_POWER_CNF, &[0x00]);
        imu.bmi270_write(ADDR_INIT_CTRL, &[0x00]);
        std::thread::sleep(Duration::from_millis(100)); // 保证初始化时间
        imu.bmi270_write(ADDR_INIT_WRITE, &BMI270_CONFIG_FILE);
        imu.bmi270_write(ADDR_INIT_CTRL, &[0x01]);
        std::thread::sleep(Duration::from_secs(2)); // 保证初始化时间

        // 检测初始化状态
        while imu.bmi270_read_write(ADDR_INIT_CHECK, 1)[0] & 0x01 == 0 {
            println!("初始化失败,继续等待可能会完成初始化,检测配置文件或者延长等待时间");
        }

        if config.acc_ord.is_some() {
            // 有一个就代表都配置了,启动加速度计,默认高性能
            unsafe {
                let conf_value = (0_u8 | config.acc_ord.unwrap_unchecked() as u8)
                    | (0_u8 | ((config.acc_bwp.unwrap_unchecked() as u8) << 4)) & 0x80;

                imu.bmi270_write(ADDR_ACC_CONF, &[conf_value]);
                imu.bmi270_write(ADDR_ACC_RANGE, &[config.acc_range.unwrap_unchecked() as u8]);
            }
        }

        if config.gyr_ord.is_some() {
            // 有一个就代表都配置了,启动加速度计,默认高性能
            unsafe {
                let conf_value = (0_u8 | config.gyr_ord.unwrap_unchecked() as u8)
                    | (0_u8 | ((config.gyr_bwp.unwrap_unchecked() as u8) << 4)) & 0xc0;

                imu.bmi270_write(ADDR_GYR_CONF, &[conf_value]);
                imu.bmi270_write(ADDR_GYR_RANGE, &[config.gyr_range.unwrap_unchecked() as u8]);
            }
        }

        // 默认开启所有传感器(AUX除外)
        imu.bmi270_write(ADDR_POWER_CTRL, &[0x0e]);
        unsafe {
            imu.acc_range = config.acc_range.unwrap_unchecked().to_sensitivity();
            imu.gyr_range = config.gyr_range.unwrap_unchecked().to_sensitivity();
        }
        imu
    }
}

impl Bmi270Data for Bmi270<Spidev> {
    fn acc(&mut self) -> [f64; 3] {
        unsafe {
            self.bmi270_read_write(ADDR_ACC_DATA, 6)
                .chunks_exact(2)
                .map(|blocks| {
                    i16::from_le_bytes(blocks.try_into().unwrap_unchecked()) as f64 / self.acc_range
                })
                .collect::<Vec<f64>>()
                .try_into()
                .unwrap()
        }
    }
    fn acc_gyr(&mut self) -> [f64; 6] {
        unsafe {
            let mut f: [f64; 6] = self
                .bmi270_read_write(ADDR_ACC_DATA, 12)
                .chunks_exact(2)
                .map(|blocks| i16::from_le_bytes(blocks.try_into().unwrap_unchecked()) as f64)
                .collect::<Vec<f64>>()
                .try_into()
                .unwrap();
            f[0] = f[0] / self.acc_range;
            f[1] = f[1] / self.acc_range;
            f[2] = f[2] / self.acc_range;
            f[3] = f[3] / self.gyr_range;
            f[4] = f[4] / self.gyr_range;
            f[5] = f[5] / self.gyr_range;
            f
        }
    }
    fn gyr(&mut self) -> [f64; 3] {
        unsafe {
            self.bmi270_read_write(ADDR_GYR_DATA, 6)
                .chunks_exact(2)
                .map(|blocks| {
                    i16::from_le_bytes(blocks.try_into().unwrap_unchecked()) as f64 / self.gyr_range
                })
                .collect::<Vec<f64>>()
                .try_into()
                .unwrap()
        }
    }
    fn temperature(&mut self) -> f64 {
        todo!()
    }
}