//! This example test the RP Pico on board LED.
//!
//! It does not work with the RP Pico W board. See wifi_blinky.rs.

#![no_std]
#![no_main]

use embassy_usb::{Builder, Config, class::cdc_acm::{CdcAcmClass, State}, UsbDevice, driver::EndpointError};
use embassy_executor::Spawner;
use embassy_rp::{bind_interrupts, gpio, peripherals::USB, usb::{Driver, Instance, InterruptHandler}};
use embassy_time::Timer;
use gpio::{Level, Output};
use defmt::{info, panic, unwrap};
use defmt_rtt as _;
use panic_probe as _;
use static_cell::StaticCell;

bind_interrupts!(struct Irqs {
    USBCTRL_IRQ => InterruptHandler<USB>;
});

#[embassy_executor::task]
async fn usb_task(mut usb: UsbDevice<'static, Driver<'static, USB>>) {
    usb.run().await
}

#[embassy_executor::main]
async fn main(spawner: Spawner) {
    let p = embassy_rp::init(Default::default());
    let mut led = Output::new(p.PIN_15, Level::Low);

    let driver = embassy_rp::usb::Driver::new(p.USB, Irqs);
    let mut config = Config::new(0x9988, 0x8899);
    config.manufacturer = Some("Savanni");
    config.product = Some("USB test device");
    config.serial_number = Some("abcdefg");
    config.max_power = 100;
    config.max_packet_size_0 = 64;

    let mut builder = {
        static CONFIG_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new();
        static BOS_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new();
        static CONTROL_BUF: StaticCell<[u8; 64]> = StaticCell::new();

        let mut builder = Builder::new(
            driver,
            config,
            CONFIG_DESCRIPTOR.init([0; 256]),
            BOS_DESCRIPTOR.init([0; 256]),
            &mut [],
            CONTROL_BUF.init([0; 64]),
            );
        builder
    };

    let mut class = {
        static STATE: StaticCell<State> = StaticCell::new();
        let state = STATE.init(State::new());
        CdcAcmClass::new(&mut builder, state, 64)
    };

    let usb = builder.build();

    unwrap!(spawner.spawn(usb_task(usb)));

    loop {
        class.wait_connection().await;
        led.set_high();
        info!("Connected");
        // let _ = echo(&mut class).await;
        let _ = primes(&mut class).await;
        info!("Disconnected");
        led.set_low();
    }
}

struct Disconnected {}

impl From<EndpointError> for Disconnected {
    fn from(val: EndpointError) -> Self {
        match val {
            EndpointError::BufferOverflow => {
                panic!("Buffer overflow");
            }
            EndpointError::Disabled => Disconnected{},
        }
    }
}

async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {
    let mut buf = [0; 64];
    loop {
        let n = class.read_packet(&mut buf).await?;
        buf[n] = b'\r';
        buf[n+1] = b'\n';
        let data = &buf[..n+2];
        info!("data: {:x}", data);
        class.write_packet(&data).await?;
    }
}

async fn primes<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {
    let PRIMES: [u8; 55] = [1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251];
    let mut buf = [0; 64];
    loop {
        for idx in 0..PRIMES.len() {
            buf[0] = PRIMES[idx];
            class.write_packet(&buf).await?;
            Timer::after_secs(1).await;
        };
    }
}