#![no_std]
#![no_main]

// Ensure we halt the program on panic (if we don't mention this crate it won't
// be linked)
use panic_halt as _;

use rp_pico as bsp;

use bsp::{
    entry,
    hal::{
        self, pac,
        pac::{interrupt, Interrupt},
        prelude::*,
        usb::UsbBus,
    },
};

use cortex_m::delay::Delay;

use usb_device::{class_prelude::*, prelude::*};
use usbd_hid::{
    descriptor::{MouseReport, SerializedDescriptor},
    hid_class::HIDClass,
};

/// The USB Device Driver (shared with the interrupt).
static mut USB_DEVICE: Option<UsbDevice<UsbBus>> = None;

/// The USB Bus Driver (shared with the interrupt).
static mut USB_BUS: Option<UsbBusAllocator<UsbBus>> = None;

/// The USB Human Interface Device Driver (shared with the interrupt).
static mut USB_HID: Option<HIDClass<UsbBus>> = None;

#[entry]
fn main() -> ! {
    // Grab our singleton objects
    let mut pac = pac::Peripherals::take().unwrap();
    let core = pac::CorePeripherals::take().unwrap();

    // Set up the watchdog driver - needed by the clock setup code
    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);

    // Configure the clocks
    //
    // The default is to generate a 125 MHz system clock
    let clocks = hal::clocks::init_clocks_and_plls(
        rp_pico::XOSC_CRYSTAL_FREQ,
        pac.XOSC,
        pac.CLOCKS,
        pac.PLL_SYS,
        pac.PLL_USB,
        &mut pac.RESETS,
        &mut watchdog,
    )
    .ok()
    .unwrap();

    unsafe {
        USB_BUS = Some(UsbBusAllocator::new(UsbBus::new(
            pac.USBCTRL_REGS,
            pac.USBCTRL_DPRAM,
            clocks.usb_clock,
            true,
            &mut pac.RESETS,
        )))
    };

    unsafe {
        USB_HID = Some(HIDClass::new(
            USB_BUS.as_ref().unwrap(),
            MouseReport::desc(),
            60,
        ));
    }

    unsafe {
        USB_DEVICE = Some(
            UsbDeviceBuilder::new(USB_BUS.as_ref().unwrap(), UsbVidPid(0x046d, 0x101b))
                .manufacturer("Logitech")
                .product("Marathon Mouse/Performance Plus M705")
                .serial_number("B14D65DA")
                .build(),
        );
    }

    unsafe {
        pac::NVIC::unmask(Interrupt::USBCTRL_IRQ);
    }

    let mut delay = Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());

    loop {
        mouse_move(&mut delay, 4);
        mouse_move(&mut delay, -4);
    }
}

fn mouse_move(delay: &mut Delay, v: i8) {
    delay.delay_ms(100);

    let rep = MouseReport {
        x: 0,
        y: v,
        buttons: 0,
        wheel: 0,
        pan: 0,
    };
    push_mouse_movement(rep).unwrap_or(0);
}

/// with interrupts disabled, to avoid a race hazard with the USB IRQ.
fn push_mouse_movement(report: MouseReport) -> Result<usize, usb_device::UsbError> {
    critical_section::with(|_| unsafe { USB_HID.as_mut().map(|hid| hid.push_input(&report)) })
        .unwrap()
}

#[allow(non_snake_case)]
#[interrupt]
unsafe fn USBCTRL_IRQ() {
    // Handle USB request
    let usb_dev = USB_DEVICE.as_mut().unwrap();
    let usb_hid = USB_HID.as_mut().unwrap();
    usb_dev.poll(&mut [usb_hid]);
}