//! Test we can talk to the BLE controller.
//!
//! We just send a reset command and check that we get a command complete for
//! the command.
//!
//! This is at least enough to know BLE is initialized find and the controller
//! responds as expected.
//!
//! See Bluetooth Core Specification v5.3
//!
//! THIS DOESN'T ACTUALLY TEST THE RADIO HOWEVER.

//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s3
//% FEATURES: unstable esp-wifi esp-alloc esp-wifi/ble

#![no_std]
#![no_main]

use embedded_io::{Read, Write};
use esp_hal::{clock::CpuClock, peripherals::Peripherals, rng::Rng, timer::timg::TimerGroup};
use esp_wifi::ble::controller::BleConnector;
use hil_test as _;

#[cfg(test)]
#[embedded_test::tests(default_timeout = 3)]
mod tests {
    use super::*;

    const H4_TYPE_COMMAND: u8 = 1;

    pub const CONTROLLER_OGF: u8 = 0x03;
    pub const RESET_OCF: u16 = 0x03;

    #[init]
    fn init() -> Peripherals {
        esp_alloc::heap_allocator!(size: 72 * 1024);

        let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
        esp_hal::init(config)
    }

    #[test]
    fn test_controller_comms(mut peripherals: Peripherals) {
        let timg0 = TimerGroup::new(peripherals.TIMG0);
        let init = esp_wifi::init(
            timg0.timer0,
            Rng::new(peripherals.RNG),
            peripherals.RADIO_CLK,
        )
        .unwrap();

        let mut connector = BleConnector::new(&init, &mut peripherals.BT);

        // send reset cmd
        pub const fn opcode(ogf: u8, ocf: u16) -> [u8; 2] {
            let opcode = ((ogf as u16) << 10) + ocf as u16;
            opcode.to_le_bytes()
        }

        let opcode = opcode(CONTROLLER_OGF, RESET_OCF);

        let mut buf = [0u8; 4];
        buf[0] = H4_TYPE_COMMAND;
        buf[1] = opcode[0];
        buf[2] = opcode[1];
        buf[3] = 0; // payload length - RESET COMMAND doesn't have any parameters

        connector.write(&buf).unwrap();

        // wait for an expected response - we might get some other events, too, since
        // different chips have different defaults for which events are masked away
        // so if we don't get the expected response we'll timeout and fail
        let mut buf = [0u8; 255];
        loop {
            let len = read_packet(&mut connector, &mut buf);
            if len == 7 {
                assert_eq!(buf[0], 4, "Expected packet type = 4 (EVENT)");
                assert_eq!(buf[1], 14, "Expected event code = 14 (COMMAND_COMPLETE)");
                assert_eq!(buf[2], 4, "Expected payload length = 4");

                // don't care about NUM_HANDLES (different for different chips)

                assert_eq!(buf[4], opcode[0], "Unexpected op-code first byte");
                assert_eq!(buf[5], opcode[1], "Unexpected op-code second byte");
                assert_eq!(
                    buf[6], 0,
                    "Expected RESET COMMAND to succeed (return code = 0)"
                );
                break;
            }
        }
    }
}

fn read_packet(connector: &mut BleConnector, buf: &mut [u8]) -> usize {
    // Read header
    read_all(connector, &mut buf[..3]);

    // Read payload
    let payload_len = buf[2] as usize;
    read_all(connector, &mut buf[3..][..payload_len]);

    3 + payload_len
}

fn read_all(connector: &mut BleConnector, mut buf: &mut [u8]) {
    while !buf.is_empty() {
        let len = connector.read(buf).unwrap();
        buf = &mut buf[len..];
    }
}
