use hal::prelude::*;
use panic_halt as _;
use stm32f0xx_hal as hal;

pub 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::PushPull>>,
    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>>,
}

impl Bus {
    pub fn new(
        latch_en: hal::gpio::gpioa::PA0<hal::gpio::Input<hal::gpio::Floating>>,
        select_en: hal::gpio::gpiob::PB8<hal::gpio::Input<hal::gpio::Floating>>,
        pb0: hal::gpio::gpiob::PB0<hal::gpio::Input<hal::gpio::Floating>>,
        pb1: hal::gpio::gpiob::PB1<hal::gpio::Input<hal::gpio::Floating>>,
        pb2: hal::gpio::gpiob::PB2<hal::gpio::Input<hal::gpio::Floating>>,
        pb3: hal::gpio::gpiob::PB3<hal::gpio::Input<hal::gpio::Floating>>,
        pb4: hal::gpio::gpiob::PB4<hal::gpio::Input<hal::gpio::Floating>>,
        pb5: hal::gpio::gpiob::PB5<hal::gpio::Input<hal::gpio::Floating>>,
        pb6: hal::gpio::gpiob::PB6<hal::gpio::Input<hal::gpio::Floating>>,
        pb7: hal::gpio::gpiob::PB7<hal::gpio::Input<hal::gpio::Floating>>,
    ) -> Self {
        cortex_m::interrupt::free(|cs| {
            Self {
                latch_en: latch_en.into_push_pull_output(cs),
                select_en: select_en.into_push_pull_output(cs),
                pb0: pb0.into_push_pull_output(cs),
                pb1: pb1.into_push_pull_output(cs),
                pb2: pb2.into_push_pull_output(cs),
                pb3: pb3.into_push_pull_output(cs),
                pb4: pb4.into_push_pull_output(cs),
                pb5: pb5.into_push_pull_output(cs),
                pb6: pb6.into_push_pull_output(cs),
                pb7: pb7.into_push_pull_output(cs),
            }
        })
    }

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

            _ => self.select(2),
        }
        self.select_en(true);
        self.write(data);
        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) {
        self.write(line);
        self.latch_en.set_high().ok();
        self.latch_en.set_low().ok();
    }
}
