#![no_std]
#![no_main]

use cortex_m_rt::entry;
use hal::prelude::*;
use hal::stm32;
use panic_halt as _;
use stm32f0xx_hal as hal;

use defmt::info;
use defmt_rtt as _;

use embedded_midi::Channel;
use hal::{delay::Delay, serial::Serial};

defmt::timestamp!("{=u32}", {
    static mut COUNTER: u32 = 0;
    unsafe {
        COUNTER = COUNTER.wrapping_add(1);
        COUNTER
    }
});

struct Bus {
    latch_en: hal::gpio::gpioa::PA0<hal::gpio::Output<hal::gpio::PushPull>>,
    select_en: hal::gpio::gpiob::PB8<hal::gpio::Output<hal::gpio::OpenDrain>>,
    pb0: hal::gpio::gpiob::PB0<hal::gpio::Output<hal::gpio::PushPull>>,
    pb1: hal::gpio::gpiob::PB1<hal::gpio::Output<hal::gpio::PushPull>>,
    pb2: hal::gpio::gpiob::PB2<hal::gpio::Output<hal::gpio::PushPull>>,
    pb3: hal::gpio::gpiob::PB3<hal::gpio::Output<hal::gpio::PushPull>>,
    pb4: hal::gpio::gpiob::PB4<hal::gpio::Output<hal::gpio::PushPull>>,
    pb5: hal::gpio::gpiob::PB5<hal::gpio::Output<hal::gpio::PushPull>>,
    pb6: hal::gpio::gpiob::PB6<hal::gpio::Output<hal::gpio::PushPull>>,
    pb7: hal::gpio::gpiob::PB7<hal::gpio::Output<hal::gpio::PushPull>>,
}

#[entry]
fn main() -> ! {
    // Get device peripherals
    let mut dp = stm32::Peripherals::take().unwrap();
    let cp = cortex_m::Peripherals::take().unwrap();

    // Configure the clock to 48 MHz
    let mut rcc = dp
        .RCC
        .configure()
        .hsi48()
        .enable_crs(dp.CRS)
        .sysclk(48.mhz())
        .pclk(24.mhz())
        .freeze(&mut dp.FLASH);

    // Set up delay provider
    let mut delay = Delay::new(cp.SYST, &rcc);

    // Configure GPIO
    let gpioa = dp.GPIOA.split(&mut rcc);
    let gpiob = dp.GPIOB.split(&mut rcc);

    let button_1_5 = cortex_m::interrupt::free(|cs| gpiob.pb13.into_floating_input(cs));
    let button_6_10 = cortex_m::interrupt::free(|cs| gpiob.pb11.into_floating_input(cs));

    let mut bus = cortex_m::interrupt::free(|cs| Bus {
        latch_en: gpioa.pa0.into_push_pull_output(cs),
        select_en: gpiob.pb8.into_open_drain_output(cs),
        pb0: gpiob.pb0.into_push_pull_output(cs),
        pb1: gpiob.pb1.into_push_pull_output(cs),
        pb2: gpiob.pb2.into_push_pull_output(cs),
        pb3: gpiob.pb3.into_push_pull_output(cs),
        pb4: gpiob.pb4.into_push_pull_output(cs),
        pb5: gpiob.pb5.into_push_pull_output(cs),
        pb6: gpiob.pb6.into_push_pull_output(cs),
        pb7: gpiob.pb7.into_push_pull_output(cs),
    });

    // Configure serial port
    let serial = cortex_m::interrupt::free(|cs| {
        let tx = gpioa.pa9.into_alternate_af1(cs);
        let rx = gpioa.pa10.into_alternate_af1(cs);
        Serial::usart1(dp.USART1, (tx, rx), 31_250.bps(), &mut rcc)
    });

    let (_tx, rx) = serial.split();
    let mut midi = embedded_midi::MidiIn::new(rx);

    // Main loop
    let mut ic_03: u8 = 0;
    let mut ic_10: u8 = 0;
    let mut ic_11: u8 = 0;

    loop {
        let message = midi.read();
        match message {
            Ok(embedded_midi::MidiMessage::ControlChange(channel, control, value))
                if channel == Channel::new(0) =>
            {
                if control == embedded_midi::Control::new(20) {
                    ic_03 = value.into();
                } else if control == embedded_midi::Control::new(21) {
                    let value: u8 = value.into();
                    ic_03 = 128_u8 + value;
                } else if control == embedded_midi::Control::new(22) {
                    ic_10 = value.into();
                } else if control == embedded_midi::Control::new(23) {
                    let value: u8 = value.into();
                    ic_10 = 128_u8 + value;
                } else if control == embedded_midi::Control::new(24) {
                    ic_11 = value.into();
                } else if control == embedded_midi::Control::new(25) {
                    let value: u8 = value.into();
                    ic_11 = 128_u8 + value;
                }
                bus.output(03, ic_03, &mut delay);
                bus.output(10, ic_10, &mut delay);
                bus.output(11, ic_11, &mut delay);
            }
            Ok(_) => {
                info!("unhandled midi message");
            }
            Err(nb::Error::WouldBlock) => {}
            Err(nb::Error::Other(hal::serial::Error::Framing)) => {
                info!("Error::Framing");
            }
            Err(nb::Error::Other(hal::serial::Error::Noise)) => {
                info!("Error::Noise");
            }
            Err(nb::Error::Other(hal::serial::Error::Overrun)) => {
                info!("Error::Overrun");
            }
            Err(nb::Error::Other(hal::serial::Error::Parity)) => {
                info!("Error::Parity");
            }
            Err(nb::Error::Other(_)) => {
                info!("Error::Other");
            }
        }
        if button_1_5.is_high().unwrap_or(false) {
            info!("Button 1-5 pressed");
            delay.delay_ms(500u16);
        }

        if button_6_10.is_high().unwrap_or(false) {
            info!("Button 6-10 pressed");
            delay.delay_ms(500u16);
        }
    }
}

impl Bus {
    fn output(&mut self, ic: u8, data: u8, delay: &mut Delay) {
        match ic {
            0 | 3 => self.select(1, delay),
            1 | 10 => self.select(0, delay),
            2 | 11 => self.select(5, delay),

            _ => self.select(2, delay),
        }
        delay.delay_us(1u16);
        self.select_en(true);
        delay.delay_us(1u16);
        self.write(data);
        delay.delay_us(1u16);
        self.select_en(false);
    }

    fn write(&mut self, data: u8) {
        self.pb0.set_state((data & 0b_0000_0001 != 0).into()).ok();
        self.pb1.set_state((data & 0b_0000_0010 != 0).into()).ok();
        self.pb2.set_state((data & 0b_0000_0100 != 0).into()).ok();
        self.pb3.set_state((data & 0b_0000_1000 != 0).into()).ok();
        self.pb4.set_state((data & 0b_0001_0000 != 0).into()).ok();
        self.pb5.set_state((data & 0b_0010_0000 != 0).into()).ok();
        self.pb6.set_state((data & 0b_0100_0000 != 0).into()).ok();
        self.pb7.set_state((data & 0b_1000_0000 != 0).into()).ok();
    }

    fn select_en(&mut self, enable: bool) {
        self.select_en.set_state(enable.into()).ok();
    }

    fn select(&mut self, line: u8, delay: &mut Delay) {
        self.write(line);
        delay.delay_us(1u16);
        self.latch_en.set_high().ok();
        delay.delay_us(1u16);
        self.latch_en.set_low().ok();
    }
}
