#![no_std]

extern crate cortex_m;
extern crate embedded_hal;
extern crate gemma_m0 as hal;
extern crate panic_halt;

pub use cortex_m::asm::delay;
pub use hal::entry;

use core::u8;
use embedded_hal::digital::OutputPin;
use hal::{
    clock::GenericClockController,
    Peripherals
};
use lights::{
    HardwareRgb,
    Lights
};

pub fn boot() -> impl Lights<Pixel = HardwareRgb> {
    let mut peripherals = Peripherals::take().unwrap();
    let _clock = GenericClockController::with_internal_32kosc(
        peripherals.GCLK,
        &mut peripherals.PM,
        &mut peripherals.SYSCTRL,
        &mut peripherals.NVMCTRL
    );

    let mut pins = hal::Pins::new(peripherals.PORT);

    NeopixelLights {
        out: pins.d1.into_push_pull_output(&mut pins.port)
    }
}

// approx. 20ns per clock cycle;
const ZERO_HIGH_CYCLES: u32 = 11; // smidge above 200ns
const ONE_HIGH_CYCLES: u32 = 35; // about 700ns
const DATA_LOW_CYCLES: u32 = 25; // about 500ns
const LATCH_CYCLES: u32 = 300; // about 6000ns
//const FRAME_DELAY_CYCLES: u32 = 1_000_000; // about 20ms, but doesn't count the time for the other operations

pub struct NeopixelLights<T: OutputPin> {
    out: T
}

impl<T: OutputPin> NeopixelLights<T> {
    #[inline]
    fn write(&mut self, bit: bool) {
        self.out.set_high();
        delay(if bit { ONE_HIGH_CYCLES } else { ZERO_HIGH_CYCLES });
        self.out.set_low();
        delay(DATA_LOW_CYCLES);
    }

    #[inline]
    fn byte(&mut self, byte: u8) {
        self.write(byte & 0x80 != 0);
        self.write(byte & 0x40 != 0);
        self.write(byte & 0x20 != 0);
        self.write(byte & 0x10 != 0);
        self.write(byte & 0x08 != 0);
        self.write(byte & 0x04 != 0);
        self.write(byte & 0x02 != 0);
        self.write(byte & 0x01 != 0);
    }
}

impl<T: OutputPin> Lights for NeopixelLights<T> {
    type Pixel = HardwareRgb;

    #[inline]
    fn render(&mut self, rgb: &HardwareRgb) {
        // GRB pixel order
        self.byte(rgb.1);
        self.byte(rgb.0);
        self.byte(rgb.2);
    }
    fn latch(&mut self) {
        delay(LATCH_CYCLES);
    }
}