/*
For a detailed explanation of this code check out the associated blog post:
https://apollolabsblog.hashnode.dev/esp32-standard-library-embedded-rust-gpio-control

GitHub Repo containing source code and other examples:
https://github.com/apollolabsdev

For notifications on similar examples and more, subscribe to newsletter here:
https://www.theembeddedrustacean.com/subscribe
*/

use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported

use esp_idf_hal::delay::FreeRtos;
use esp_idf_hal::gpio::*;
use esp_idf_hal::peripherals::Peripherals;

fn main() {
    // It is necessary to call this function once. Otherwise some patches to the runtime
    // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
    esp_idf_sys::link_patches();

    // Take Peripherals
    let dp = Peripherals::take().unwrap();

    // Configure all LED pins to digital outputs
    // let mut led1 = PinDriver::output(dp.pins.gpio1).unwrap();
    // let mut led2 = PinDriver::output(dp.pins.gpio10).unwrap();
    // let mut led3 = PinDriver::output(dp.pins.gpio19).unwrap();
    // let mut led4 = PinDriver::output(dp.pins.gpio18).unwrap();
    // let mut led5 = PinDriver::output(dp.pins.gpio4).unwrap();
    // let mut led6 = PinDriver::output(dp.pins.gpio5).unwrap();
    // let mut led7 = PinDriver::output(dp.pins.gpio6).unwrap();
    // let mut led8 = PinDriver::output(dp.pins.gpio7).unwrap();
    // let mut led9 = PinDriver::output(dp.pins.gpio8).unwrap();
    // let mut led10 = PinDriver::output(dp.pins.gpio9).unwrap();

    // Config Option 2 (Optimized Appraoch)
    let mut leds = [
        PinDriver::output(dp.pins.gpio1.downgrade_output()).unwrap(),
        PinDriver::output(dp.pins.gpio10.downgrade_output()).unwrap(),
        PinDriver::output(dp.pins.gpio19.downgrade_output()).unwrap(),
        PinDriver::output(dp.pins.gpio18.downgrade_output()).unwrap(),
        PinDriver::output(dp.pins.gpio4.downgrade_output()).unwrap(),
        PinDriver::output(dp.pins.gpio5.downgrade_output()).unwrap(),
        PinDriver::output(dp.pins.gpio6.downgrade_output()).unwrap(),
        PinDriver::output(dp.pins.gpio7.downgrade_output()).unwrap(),
        PinDriver::output(dp.pins.gpio8.downgrade_output()).unwrap(),
        PinDriver::output(dp.pins.gpio9.downgrade_output()).unwrap(),
    ];

    // Configure Button pin to input with Pull Up
    let mut button = PinDriver::input(dp.pins.gpio3).unwrap();
    button.set_pull(Pull::Up).unwrap();

    // Initialize variable with starting delay
    let mut blinkdelay = 200_u32;

    loop {
        // Algo:
        // Starting with first LED in sequence
        // 1. Turn on LED
        // 2. Retrieve adjusted delay based on button press
        // 3. Delay with adjusted value
        // 4. Turn off LED
        // 5. Delay for 100ms (to make sure LED is turned off)
        // 6. Repeat steps 1-5 for next LED in sequence
        // 7. Once all LEDs are done loop back to first LED in sequence

        // // LED 1
        // led1.set_high().unwrap();
        // blinkdelay = button_pressed(&button, &blinkdelay);
        // FreeRtos::delay_ms(blinkdelay);
        // led1.set_low().unwrap();
        // FreeRtos::delay_ms(100_u32);

        // // LED 2
        // led2.set_high().unwrap();
        // blinkdelay = button_pressed(&button, &blinkdelay);
        // FreeRtos::delay_ms(blinkdelay);
        // led2.set_low().unwrap();
        // FreeRtos::delay_ms(100_u32);

        // // LED 3
        // led3.set_high().unwrap();
        // blinkdelay = button_pressed(&button, &blinkdelay);
        // FreeRtos::delay_ms(blinkdelay);
        // led3.set_low().unwrap();
        // FreeRtos::delay_ms(100_u32);

        // // LED 4
        // led4.set_high().unwrap();
        // blinkdelay = button_pressed(&button, &blinkdelay);
        // FreeRtos::delay_ms(blinkdelay);
        // led4.set_low().unwrap();
        // FreeRtos::delay_ms(100_u32);

        // // LED 5
        // led5.set_high().unwrap();
        // blinkdelay = button_pressed(&button, &blinkdelay);
        // FreeRtos::delay_ms(blinkdelay);
        // led5.set_low().unwrap();
        // FreeRtos::delay_ms(100_u32);

        // // LED 6
        // led6.set_high().unwrap();
        // blinkdelay = button_pressed(&button, &blinkdelay);
        // FreeRtos::delay_ms(blinkdelay);
        // led6.set_low().unwrap();
        // FreeRtos::delay_ms(100_u32);

        // // LED 7
        // led7.set_high().unwrap();
        // blinkdelay = button_pressed(&button, &blinkdelay);
        // FreeRtos::delay_ms(blinkdelay);
        // led7.set_low().unwrap();
        // FreeRtos::delay_ms(100_u32);

        // // LED 8
        // led8.set_high().unwrap();
        // blinkdelay = button_pressed(&button, &blinkdelay);
        // FreeRtos::delay_ms(blinkdelay);
        // led8.set_low().unwrap();
        // FreeRtos::delay_ms(100_u32);

        // // LED 9
        // led9.set_high().unwrap();
        // blinkdelay = button_pressed(&button, &blinkdelay);
        // FreeRtos::delay_ms(blinkdelay);
        // led9.set_low().unwrap();
        // FreeRtos::delay_ms(100_u32);

        // // LED 10
        // led10.set_high().unwrap();
        // blinkdelay = button_pressed(&button, &blinkdelay);
        // FreeRtos::delay_ms(blinkdelay);
        // led10.set_low().unwrap();
        // FreeRtos::delay_ms(100_u32);

        // Option 2 (Optimized)
        for mut led in &mut leds {
            led.set_high().unwrap();
            blinkdelay = button_pressed(&button, &blinkdelay);
            FreeRtos::delay_ms(blinkdelay);
            led.set_low().unwrap();
            FreeRtos::delay_ms(100_u32);
        }
    }
}

fn button_pressed(but: &PinDriver<'_, Gpio3, Input>, del: &u32) -> u32 {
    // Check if Button has been pressed
    // If not pressed, return the delay value unchanged
    if but.is_low() {
        // if the value of the delay passed is less of equal to 50 then reset it to initial value
        // else subtract 50 from the passed delay
        println!("Button Pressed!");
        if del <= &50_u32 {
            return 200_u32;
        } else {
            return del - 50_u32;
        }
    } else {
        return *del;
    }
}