#![deny(unsafe_code)]
#![allow(clippy::empty_loop)]
#![no_main]
#![no_std]

// Halt on panic
use panic_halt as _; // panic handler

use cortex_m_rt::entry;
use embedded_hal::digital::OutputPin;
use stm32f4xx_hal::timer::SysDelay;
use stm32f4xx_hal::{self as hal};

use crate::hal::{pac, prelude::*};

mod leds;
use crate::leds::{Leds, Toggle, ToggleLed};
mod state_machine;
use crate::state_machine::StateMachine;

fn error_blink<T: OutputPin>(led: &mut ToggleLed<T>, delay: &mut SysDelay) -> ! {
    loop {
        let _ = led.toggle();
        delay.delay_ms(10_u32);
    }
}

#[entry]
fn main() -> ! {
    if let (Some(dp), Some(cp)) = (
        pac::Peripherals::take(),
        cortex_m::peripheral::Peripherals::take(),
    ) {
        //Klok instellen
        let rcc = dp.RCC.constrain();
        let clocks = rcc.cfgr.sysclk(48.MHz()).freeze();
        // Create a delay abstraction based on SysTick
        let mut delay = cp.SYST.delay(&clocks);

        //GPIOD ophalen
        let gpiod = dp.GPIOD.split();
        //led pinnen uphalen
        let mut green_pin = &mut gpiod.pd12.into_push_pull_output();
        let mut red_pin = &mut gpiod.pd14.into_push_pull_output();
        let mut orange_pin = &mut gpiod.pd13.into_push_pull_output();
        let mut blue_pin = &mut gpiod.pd15.into_push_pull_output();
        let mut blue = ToggleLed::new(&mut blue_pin).expect("faild to set blue led");
        let mut green = match ToggleLed::new(&mut green_pin) {
            Err(_) => error_blink(&mut blue, &mut delay),
            Ok(green) => green
        };
        let mut red = match ToggleLed::new(&mut red_pin) {
            Err(_) => error_blink(&mut blue, &mut delay),
            Ok(red) => red
        };
        let mut orange = match ToggleLed::new(&mut orange_pin) {
            Err(_) => error_blink(&mut blue, &mut delay),
            Ok(orange) => orange
        };
        let mut leds = Leds::new(&mut red, &mut green, &mut orange, &mut blue);
        //user knop
        let gpioa = dp.GPIOA.split();
        let button = &mut gpioa.pa0.into_floating_input();
        //leds struct en state machine maken
        let mut state_machine = match StateMachine::new(&mut leds) {
            Err(_err) => error_blink(&mut blue, &mut delay),
            Ok(state_machine) => state_machine,
        };

        let mut button_high = button.is_high();
        loop {
            if let Err(_) = state_machine.second_passed() {
                error_blink(&mut blue, &mut delay);
            }
            for _ in 0..99 {
                if button_high != button.is_high() {
                    button_high = button.is_high();
                    if button_high {
                        if let Err(_) = state_machine.button_press() {
                            error_blink(&mut blue, &mut delay);
                        }
                    } else {
                        if let Err(_) = state_machine.button_release() {
                            error_blink(&mut blue, &mut delay);
                        }
                    }
                }
                delay.delay_ms(10_u32);
            }
        }
    }

    loop {}
}
