//! GPIO

use core::convert::Infallible;
use core::future::Future;
use core::marker::PhantomData;
use core::pin::Pin as FuturePin;
use core::task::{Context, Poll};

use embassy_hal_internal::interrupt::InterruptExt;
use embassy_sync::waitqueue::AtomicWaker;

use crate::clocks::enable_and_reset;
use crate::iopctl::IopctlPin;
pub use crate::iopctl::{AnyPin, DriveMode, DriveStrength, Function, Inverter, Pull, SlewRate};
use crate::sealed::Sealed;
use crate::{BitIter, Peri, PeripheralType, interrupt, peripherals};

// This should be unique per IMXRT package
const PORT_COUNT: usize = 8;

/// Digital input or output level.
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Level {
    /// Logic Low
    Low,
    /// Logic High
    High,
}

impl From<bool> for Level {
    fn from(val: bool) -> Self {
        match val {
            true => Self::High,
            false => Self::Low,
        }
    }
}

impl From<Level> for bool {
    fn from(level: Level) -> bool {
        match level {
            Level::Low => false,
            Level::High => true,
        }
    }
}

/// Interrupt trigger levels.
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InterruptType {
    /// Trigger on level.
    Level,
    /// Trigger on edge.
    Edge,
}

#[cfg(feature = "rt")]
#[interrupt]
#[allow(non_snake_case)]
fn GPIO_INTA() {
    irq_handler(&GPIO_WAKERS);
}

#[cfg(feature = "rt")]
fn irq_handler(port_wakers: &[Option<&PortWaker>]) {
    let reg = unsafe { crate::pac::Gpio::steal() };

    for (port, port_waker) in port_wakers.iter().enumerate() {
        let Some(port_waker) = port_waker else {
            continue;
        };

        let stat = reg.intstata(port).read().bits();
        for pin in BitIter(stat) {
            // Clear the interrupt from this pin
            reg.intstata(port).write(|w| unsafe { w.status().bits(1 << pin) });
            // Disable interrupt from this pin
            reg.intena(port)
                .modify(|r, w| unsafe { w.int_en().bits(r.int_en().bits() & !(1 << pin)) });

            let Some(waker) = port_waker.get_waker(pin as usize) else {
                continue;
            };

            waker.wake();
        }
    }
}

/// Initialization Logic
/// Note: GPIO port clocks are initialized in the clocks module.
pub(crate) fn init() {
    // Enable GPIO clocks
    enable_and_reset::<peripherals::HSGPIO0>();
    enable_and_reset::<peripherals::HSGPIO1>();
    enable_and_reset::<peripherals::HSGPIO2>();
    enable_and_reset::<peripherals::HSGPIO3>();
    enable_and_reset::<peripherals::HSGPIO4>();
    enable_and_reset::<peripherals::HSGPIO5>();
    enable_and_reset::<peripherals::HSGPIO6>();
    enable_and_reset::<peripherals::HSGPIO7>();

    // Enable INTA
    interrupt::GPIO_INTA.unpend();

    // SAFETY:
    //
    // At this point, all GPIO interrupts are masked. No interrupts
    // will trigger until a pin is configured as Input, which can only
    // happen after initialization of the HAL
    unsafe { interrupt::GPIO_INTA.enable() };
}

/// Input Sense mode.
pub trait Sense: Sealed {}

/// Sense Enabled Flex pin.
///
/// This is a true flex pin as the input buffer is enabled.
/// It can sense its own level when even when configured as an output pin.
pub enum SenseEnabled {}
impl Sealed for SenseEnabled {}
impl Sense for SenseEnabled {}

/// Sense Enabled Flex pin.
///
/// This is **not** a true flex pin as the input buffer is disabled.
/// It cannot be turned into an input and it cannot see its own state, but it consumes less power.
/// Consider using a sense enabled flex pin if you need to read the pin's state or turn this into an input,
/// however note that **power consumption may be increased**.
pub enum SenseDisabled {}
impl Sealed for SenseDisabled {}
impl Sense for SenseDisabled {}

/// Flex pin.
///
/// This pin can be either an input or output pin. The output level register bit will
/// remain set while not in output mode, so the pin's level will be 'remembered' when it is not in
/// output mode.
pub struct Flex<'d, S: Sense> {
    pin: Peri<'d, AnyPin>,
    _sense_mode: PhantomData<S>,
}

impl<S: Sense> Flex<'_, S> {
    /// Converts pin to output pin
    ///
    /// The pin level will be whatever was set before (or low by default). If you want it to begin
    /// at a specific level, call `set_high`/`set_low` on the pin first.
    pub fn set_as_output(&mut self, mode: DriveMode, strength: DriveStrength, slew_rate: SlewRate) {
        self.pin
            .set_pull(Pull::None)
            .set_drive_mode(mode)
            .set_drive_strength(strength)
            .set_slew_rate(slew_rate);

        self.pin.block().dirset(self.pin.port()).write(|w|
            // SAFETY: Writing a 0 to bits in this register has no effect,
            // however PAC has it marked unsafe due to using the bits() method.
            // There is not currently a "safe" method for setting a single-bit.
            unsafe { w.dirsetp().bits(1 << self.pin.pin()) });
    }

    /// Set high
    pub fn set_high(&mut self) {
        self.pin.block().set(self.pin.port()).write(|w|
            // SAFETY: Writing a 0 to bits in this register has no effect,
            // however PAC has it marked unsafe due to using the bits() method.
            // There is not currently a "safe" method for setting a single-bit.
            unsafe { w.setp().bits(1 << self.pin.pin()) });
    }

    /// Set low
    pub fn set_low(&mut self) {
        self.pin.block().clr(self.pin.port()).write(|w|
            // SAFETY: Writing a 0 to bits in this register has no effect,
            // however PAC has it marked unsafe due to using the bits() method.
            // There is not currently a "safe" method for setting a single-bit.
            unsafe { w.clrp().bits(1 << self.pin.pin()) });
    }

    /// Set level
    pub fn set_level(&mut self, level: Level) {
        match level {
            Level::High => self.set_high(),
            Level::Low => self.set_low(),
        }
    }

    /// Is the output level high?
    #[must_use]
    pub fn is_set_high(&self) -> bool {
        !self.is_set_low()
    }

    /// Is the output level low?
    #[must_use]
    pub fn is_set_low(&self) -> bool {
        (self.pin.block().set(self.pin.port()).read().setp().bits() & (1 << self.pin.pin())) == 0
    }

    /// Toggle
    pub fn toggle(&mut self) {
        self.pin.block().not(self.pin.port()).write(|w|
            // SAFETY: Writing a 0 to bits in this register has no effect,
            // however PAC has it marked unsafe due to using the bits() method.
            // There is not currently a "safe" method for setting a single-bit.
            unsafe { w.notp().bits(1 << self.pin.pin()) });
    }
}

impl<S: Sense> Drop for Flex<'_, S> {
    #[inline]
    fn drop(&mut self) {
        critical_section::with(|_| {
            self.pin.reset();
        });
    }
}

impl<'d> Flex<'d, SenseEnabled> {
    /// New flex pin.
    pub fn new_with_sense(pin: Peri<'d, impl GpioPin>) -> Self {
        pin.set_function(Function::F0)
            .disable_analog_multiplex()
            .enable_input_buffer();

        Self {
            pin: pin.into(),
            _sense_mode: PhantomData::<SenseEnabled>,
        }
    }

    /// Converts pin to input pin
    pub fn set_as_input(&mut self, pull: Pull, inverter: Inverter) {
        self.pin.set_pull(pull).set_input_inverter(inverter);

        self.pin.block().dirclr(self.pin.port()).write(|w|
                    // SAFETY: Writing a 0 to bits in this register has no effect,
                    // however PAC has it marked unsafe due to using the bits() method.
                    // There is not currently a "safe" method for setting a single-bit.
                    unsafe { w.dirclrp().bits(1 << self.pin.pin()) });
    }

    /// Converts pin to special function pin
    /// # Safety
    /// Unsafe to require justifying change from default to a special function
    ///
    pub unsafe fn set_as_special_function(&mut self, func: Function) {
        self.pin.set_function(func);
    }

    /// Is high?
    #[must_use]
    pub fn is_high(&self) -> bool {
        !self.is_low()
    }

    /// Is low?
    #[must_use]
    pub fn is_low(&self) -> bool {
        self.pin.block().b(self.pin.port()).b_(self.pin.pin()).read() == 0
    }

    /// Current level
    #[must_use]
    pub fn get_level(&self) -> Level {
        self.is_high().into()
    }

    /// Wait until the pin is high. If it is already high, return immediately.
    #[inline]
    pub async fn wait_for_high(&mut self) {
        InputFuture::new(self.pin.reborrow(), InterruptType::Level, Level::High).await;
    }

    /// Wait until the pin is low. If it is already low, return immediately.
    #[inline]
    pub async fn wait_for_low(&mut self) {
        InputFuture::new(self.pin.reborrow(), InterruptType::Level, Level::Low).await;
    }

    /// Wait for the pin to undergo a transition from low to high.
    #[inline]
    pub async fn wait_for_rising_edge(&mut self) {
        InputFuture::new(self.pin.reborrow(), InterruptType::Edge, Level::High).await;
    }

    /// Wait for the pin to undergo a transition from high to low.
    #[inline]
    pub async fn wait_for_falling_edge(&mut self) {
        InputFuture::new(self.pin.reborrow(), InterruptType::Edge, Level::Low).await;
    }

    /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
    #[inline]
    pub async fn wait_for_any_edge(&mut self) {
        if self.is_high() {
            InputFuture::new(self.pin.reborrow(), InterruptType::Edge, Level::Low).await;
        } else {
            InputFuture::new(self.pin.reborrow(), InterruptType::Edge, Level::High).await;
        }
    }

    /// Return a new Flex pin instance with level sensing disabled.
    ///
    /// Consumes less power than a flex pin with sensing enabled.
    #[must_use]
    pub fn disable_sensing(self) -> Flex<'d, SenseDisabled> {
        // Cloning the pin is ok since we consume self immediately
        let new_pin = unsafe { self.pin.clone_unchecked() };
        drop(self);
        Flex::<SenseDisabled>::new(new_pin)
    }
}

impl<'d> Flex<'d, SenseDisabled> {
    /// New flex pin.
    pub fn new(pin: Peri<'d, impl GpioPin>) -> Self {
        pin.set_function(Function::F0)
            .disable_analog_multiplex()
            .disable_input_buffer();

        Self {
            pin: pin.into(),
            _sense_mode: PhantomData::<SenseDisabled>,
        }
    }

    /// Return a new Flex pin instance with level sensing enabled.
    #[must_use]
    pub fn enable_sensing(self) -> Flex<'d, SenseEnabled> {
        // Cloning the pin is ok since we consume self immediately
        let new_pin = unsafe { self.pin.clone_unchecked() };
        drop(self);
        Flex::new_with_sense(new_pin)
    }
}

/// Input pin
pub struct Input<'d> {
    // When Input is dropped, Flex's drop() will make sure the pin is reset to its default state.
    pin: Flex<'d, SenseEnabled>,
}

impl<'d> Input<'d> {
    /// New input pin
    pub fn new(pin: Peri<'d, impl GpioPin>, pull: Pull, inverter: Inverter) -> Self {
        let mut pin = Flex::new_with_sense(pin);
        pin.set_as_input(pull, inverter);
        Self { pin }
    }

    /// Is high?
    #[must_use]
    pub fn is_high(&self) -> bool {
        self.pin.is_high()
    }

    /// Is low?
    #[must_use]
    pub fn is_low(&self) -> bool {
        self.pin.is_low()
    }

    /// Input level
    #[must_use]
    pub fn get_level(&self) -> Level {
        self.pin.get_level()
    }

    /// Wait until the pin is high. If it is already high, return immediately.
    #[inline]
    pub async fn wait_for_high(&mut self) {
        self.pin.wait_for_high().await;
    }

    /// Wait until the pin is low. If it is already low, return immediately.
    #[inline]
    pub async fn wait_for_low(&mut self) {
        self.pin.wait_for_low().await;
    }

    /// Wait for the pin to undergo a transition from low to high.
    #[inline]
    pub async fn wait_for_rising_edge(&mut self) {
        self.pin.wait_for_rising_edge().await;
    }

    /// Wait for the pin to undergo a transition from high to low.
    #[inline]
    pub async fn wait_for_falling_edge(&mut self) {
        self.pin.wait_for_falling_edge().await;
    }

    /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
    #[inline]
    pub async fn wait_for_any_edge(&mut self) {
        self.pin.wait_for_any_edge().await;
    }
}

#[must_use = "futures do nothing unless you `.await` or poll them"]
struct InputFuture<'d> {
    pin: Peri<'d, AnyPin>,
}

impl<'d> InputFuture<'d> {
    fn new(pin: Peri<'d, impl GpioPin>, int_type: InterruptType, level: Level) -> Self {
        critical_section::with(|_| {
            // Clear any existing pending interrupt on this pin
            pin.block()
                .intstata(pin.port())
                .write(|w| unsafe { w.status().bits(1 << pin.pin()) });

            /* Pin interrupt configuration */
            pin.block().intedg(pin.port()).modify(|r, w| match int_type {
                InterruptType::Edge => unsafe { w.bits(r.bits() | (1 << pin.pin())) },
                InterruptType::Level => unsafe { w.bits(r.bits() & !(1 << pin.pin())) },
            });

            pin.block().intpol(pin.port()).modify(|r, w| match level {
                Level::High => unsafe { w.bits(r.bits() & !(1 << pin.pin())) },
                Level::Low => unsafe { w.bits(r.bits() | (1 << pin.pin())) },
            });

            // Enable pin interrupt on GPIO INT A
            pin.block()
                .intena(pin.port())
                .modify(|r, w| unsafe { w.int_en().bits(r.int_en().bits() | (1 << pin.pin())) });
        });

        Self { pin: pin.into() }
    }
}

impl Future for InputFuture<'_> {
    type Output = ();

    fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        // We need to register/re-register the waker for each poll because any
        // calls to wake will deregister the waker.
        if self.pin.port() >= GPIO_WAKERS.len() {
            panic!("Invalid GPIO port index {}", self.pin.port());
        }

        let port_waker = GPIO_WAKERS[self.pin.port()];
        if port_waker.is_none() {
            panic!("Waker not present for GPIO port {}", self.pin.port());
        }

        let waker = port_waker.unwrap().get_waker(self.pin.pin());
        if waker.is_none() {
            panic!(
                "Waker not present for GPIO pin {}, port {}",
                self.pin.pin(),
                self.pin.port()
            );
        }
        waker.unwrap().register(cx.waker());

        // Double check that the pin interrut has been disabled by IRQ handler
        if self.pin.block().intena(self.pin.port()).read().bits() & (1 << self.pin.pin()) == 0 {
            Poll::Ready(())
        } else {
            Poll::Pending
        }
    }
}

/// Output pin
/// Cannot be set as an input and cannot read its own pin state!
/// Consider using a Flex pin if you want that functionality, at the cost of higher power consumption.
pub struct Output<'d> {
    // When Output is dropped, Flex's drop() will make sure the pin is reset to its default state.
    pin: Flex<'d, SenseDisabled>,
}

impl<'d> Output<'d> {
    /// New output pin
    pub fn new(
        pin: Peri<'d, impl GpioPin>,
        initial_output: Level,
        mode: DriveMode,
        strength: DriveStrength,
        slew_rate: SlewRate,
    ) -> Self {
        let mut pin = Flex::new(pin);
        pin.set_level(initial_output);
        pin.set_as_output(mode, strength, slew_rate);

        Self { pin }
    }

    /// Set high
    pub fn set_high(&mut self) {
        self.pin.set_high();
    }

    /// Set low
    pub fn set_low(&mut self) {
        self.pin.set_low();
    }

    /// Toggle
    pub fn toggle(&mut self) {
        self.pin.toggle();
    }

    /// Set level
    pub fn set_level(&mut self, level: Level) {
        self.pin.set_level(level);
    }

    /// Is set high?
    #[must_use]
    pub fn is_set_high(&self) -> bool {
        self.pin.is_set_high()
    }

    /// Is set low?
    #[must_use]
    pub fn is_set_low(&self) -> bool {
        self.pin.is_set_low()
    }
}

trait SealedPin: IopctlPin {
    fn pin_port(&self) -> usize;

    fn port(&self) -> usize {
        self.pin_port() / 32
    }

    fn pin(&self) -> usize {
        self.pin_port() % 32
    }

    fn block(&self) -> crate::pac::Gpio {
        // SAFETY: Assuming GPIO pin specific registers are only accessed through this HAL,
        // this is safe because the HAL ensures ownership or exclusive mutable references
        // to pins.
        unsafe { crate::pac::Gpio::steal() }
    }
}

/// GPIO pin trait.
#[allow(private_bounds)]
pub trait GpioPin: SealedPin + Sized + PeripheralType + Into<AnyPin> + 'static {
    /// Type-erase the pin.
    fn degrade(self) -> AnyPin {
        // SAFETY: This is only called within the GpioPin trait, which is only
        // implemented within this module on valid pin peripherals and thus
        // has been verified to be correct.
        unsafe { AnyPin::steal(self.port() as u8, self.pin() as u8) }
    }
}

impl SealedPin for AnyPin {
    fn pin_port(&self) -> usize {
        self.pin_port()
    }
}
impl GpioPin for AnyPin {}

macro_rules! impl_pin {
    ($pin_periph:ident, $pin_port:expr, $pin_no:expr) => {
        impl SealedPin for crate::peripherals::$pin_periph {
            fn pin_port(&self) -> usize {
                $pin_port * 32 + $pin_no
            }
        }
        impl GpioPin for crate::peripherals::$pin_periph {}
        impl From<crate::peripherals::$pin_periph> for AnyPin {
            fn from(value: crate::peripherals::$pin_periph) -> Self {
                value.degrade()
            }
        }
    };
}

/// Container for pin wakers
struct PortWaker {
    offset: usize,
    wakers: &'static [AtomicWaker],
}

impl PortWaker {
    fn get_waker(&self, pin: usize) -> Option<&AtomicWaker> {
        self.wakers.get(pin - self.offset)
    }
}

macro_rules! define_port_waker {
    ($name:ident, $start:expr, $end:expr) => {
        mod $name {
            static PIN_WAKERS: [super::AtomicWaker; $end - $start + 1] =
                [const { super::AtomicWaker::new() }; $end - $start + 1];
            pub static WAKER: super::PortWaker = super::PortWaker {
                offset: $start,
                wakers: &PIN_WAKERS,
            };
        }
    };
}

// GPIO port 0
define_port_waker!(port0_waker, 0, 31);
impl_pin!(PIO0_0, 0, 0);
impl_pin!(PIO0_1, 0, 1);
impl_pin!(PIO0_2, 0, 2);
impl_pin!(PIO0_3, 0, 3);
impl_pin!(PIO0_4, 0, 4);
impl_pin!(PIO0_5, 0, 5);
impl_pin!(PIO0_6, 0, 6);
impl_pin!(PIO0_7, 0, 7);
impl_pin!(PIO0_8, 0, 8);
impl_pin!(PIO0_9, 0, 9);
impl_pin!(PIO0_10, 0, 10);
impl_pin!(PIO0_11, 0, 11);
impl_pin!(PIO0_12, 0, 12);
impl_pin!(PIO0_13, 0, 13);
impl_pin!(PIO0_14, 0, 14);
impl_pin!(PIO0_15, 0, 15);
impl_pin!(PIO0_16, 0, 16);
impl_pin!(PIO0_17, 0, 17);
impl_pin!(PIO0_18, 0, 18);
impl_pin!(PIO0_19, 0, 19);
impl_pin!(PIO0_20, 0, 20);
impl_pin!(PIO0_21, 0, 21);
impl_pin!(PIO0_22, 0, 22);
impl_pin!(PIO0_23, 0, 23);
impl_pin!(PIO0_24, 0, 24);
impl_pin!(PIO0_25, 0, 25);
impl_pin!(PIO0_26, 0, 26);
impl_pin!(PIO0_27, 0, 27);
impl_pin!(PIO0_28, 0, 28);
impl_pin!(PIO0_29, 0, 29);
impl_pin!(PIO0_30, 0, 30);
impl_pin!(PIO0_31, 0, 31);

// GPIO port 1
define_port_waker!(port1_waker, 0, 31);
impl_pin!(PIO1_0, 1, 0);
impl_pin!(PIO1_1, 1, 1);
impl_pin!(PIO1_2, 1, 2);
impl_pin!(PIO1_3, 1, 3);
impl_pin!(PIO1_4, 1, 4);
impl_pin!(PIO1_5, 1, 5);
impl_pin!(PIO1_6, 1, 6);
impl_pin!(PIO1_7, 1, 7);
impl_pin!(PIO1_8, 1, 8);
impl_pin!(PIO1_9, 1, 9);
impl_pin!(PIO1_10, 1, 10);
impl_pin!(PIO1_11, 1, 11);
impl_pin!(PIO1_12, 1, 12);
impl_pin!(PIO1_13, 1, 13);
impl_pin!(PIO1_14, 1, 14);
impl_pin!(PIO1_15, 1, 15);
impl_pin!(PIO1_16, 1, 16);
impl_pin!(PIO1_17, 1, 17);
impl_pin!(PIO1_18, 1, 18);
impl_pin!(PIO1_19, 1, 19);
impl_pin!(PIO1_20, 1, 20);
impl_pin!(PIO1_21, 1, 21);
impl_pin!(PIO1_22, 1, 22);
impl_pin!(PIO1_23, 1, 23);
impl_pin!(PIO1_24, 1, 24);
impl_pin!(PIO1_25, 1, 25);
impl_pin!(PIO1_26, 1, 26);
impl_pin!(PIO1_27, 1, 27);
impl_pin!(PIO1_28, 1, 28);
impl_pin!(PIO1_29, 1, 29);
impl_pin!(PIO1_30, 1, 30);
impl_pin!(PIO1_31, 1, 31);

// GPIO port 2
define_port_waker!(port2_waker, 0, 31);
impl_pin!(PIO2_0, 2, 0);
impl_pin!(PIO2_1, 2, 1);
impl_pin!(PIO2_2, 2, 2);
impl_pin!(PIO2_3, 2, 3);
impl_pin!(PIO2_4, 2, 4);
impl_pin!(PIO2_5, 2, 5);
impl_pin!(PIO2_6, 2, 6);
impl_pin!(PIO2_7, 2, 7);
impl_pin!(PIO2_8, 2, 8);
impl_pin!(PIO2_9, 2, 9);
impl_pin!(PIO2_10, 2, 10);
impl_pin!(PIO2_11, 2, 11);
impl_pin!(PIO2_12, 2, 12);
impl_pin!(PIO2_13, 2, 13);
impl_pin!(PIO2_14, 2, 14);
impl_pin!(PIO2_15, 2, 15);
impl_pin!(PIO2_16, 2, 16);
impl_pin!(PIO2_17, 2, 17);
impl_pin!(PIO2_18, 2, 18);
impl_pin!(PIO2_19, 2, 19);
impl_pin!(PIO2_20, 2, 20);
impl_pin!(PIO2_21, 2, 21);
impl_pin!(PIO2_22, 2, 22);
impl_pin!(PIO2_23, 2, 23);
impl_pin!(PIO2_24, 2, 24);
impl_pin!(PIO2_25, 2, 25);
impl_pin!(PIO2_26, 2, 26);
impl_pin!(PIO2_27, 2, 27);
impl_pin!(PIO2_28, 2, 28);
impl_pin!(PIO2_29, 2, 29);
impl_pin!(PIO2_30, 2, 30);
impl_pin!(PIO2_31, 2, 31);

// GPIO port 3
define_port_waker!(port3_waker, 0, 31);
impl_pin!(PIO3_0, 3, 0);
impl_pin!(PIO3_1, 3, 1);
impl_pin!(PIO3_2, 3, 2);
impl_pin!(PIO3_3, 3, 3);
impl_pin!(PIO3_4, 3, 4);
impl_pin!(PIO3_5, 3, 5);
impl_pin!(PIO3_6, 3, 6);
impl_pin!(PIO3_7, 3, 7);
impl_pin!(PIO3_8, 3, 8);
impl_pin!(PIO3_9, 3, 9);
impl_pin!(PIO3_10, 3, 10);
impl_pin!(PIO3_11, 3, 11);
impl_pin!(PIO3_12, 3, 12);
impl_pin!(PIO3_13, 3, 13);
impl_pin!(PIO3_14, 3, 14);
impl_pin!(PIO3_15, 3, 15);
impl_pin!(PIO3_16, 3, 16);
impl_pin!(PIO3_17, 3, 17);
impl_pin!(PIO3_18, 3, 18);
impl_pin!(PIO3_19, 3, 19);
impl_pin!(PIO3_20, 3, 20);
impl_pin!(PIO3_21, 3, 21);
impl_pin!(PIO3_22, 3, 22);
impl_pin!(PIO3_23, 3, 23);
impl_pin!(PIO3_24, 3, 24);
impl_pin!(PIO3_25, 3, 25);
impl_pin!(PIO3_26, 3, 26);
impl_pin!(PIO3_27, 3, 27);
impl_pin!(PIO3_28, 3, 28);
impl_pin!(PIO3_29, 3, 29);
impl_pin!(PIO3_30, 3, 30);
impl_pin!(PIO3_31, 3, 31);

// GPIO port 4
define_port_waker!(port4_waker, 0, 10);
impl_pin!(PIO4_0, 4, 0);
impl_pin!(PIO4_1, 4, 1);
impl_pin!(PIO4_2, 4, 2);
impl_pin!(PIO4_3, 4, 3);
impl_pin!(PIO4_4, 4, 4);
impl_pin!(PIO4_5, 4, 5);
impl_pin!(PIO4_6, 4, 6);
impl_pin!(PIO4_7, 4, 7);
impl_pin!(PIO4_8, 4, 8);
impl_pin!(PIO4_9, 4, 9);
impl_pin!(PIO4_10, 4, 10);

// GPIO port 7
define_port_waker!(port7_waker, 24, 31);
impl_pin!(PIO7_24, 7, 24);
impl_pin!(PIO7_25, 7, 25);
impl_pin!(PIO7_26, 7, 26);
impl_pin!(PIO7_27, 7, 27);
impl_pin!(PIO7_28, 7, 28);
impl_pin!(PIO7_29, 7, 29);
impl_pin!(PIO7_30, 7, 30);
impl_pin!(PIO7_31, 7, 31);

static GPIO_WAKERS: [Option<&PortWaker>; PORT_COUNT] = [
    Some(&port0_waker::WAKER),
    Some(&port1_waker::WAKER),
    Some(&port2_waker::WAKER),
    Some(&port3_waker::WAKER),
    Some(&port4_waker::WAKER),
    None,
    None,
    Some(&port7_waker::WAKER),
];

impl embedded_hal_02::digital::v2::InputPin for Flex<'_, SenseEnabled> {
    type Error = Infallible;

    #[inline]
    fn is_high(&self) -> Result<bool, Self::Error> {
        Ok(self.is_high())
    }

    #[inline]
    fn is_low(&self) -> Result<bool, Self::Error> {
        Ok(self.is_low())
    }
}

impl<S: Sense> embedded_hal_02::digital::v2::OutputPin for Flex<'_, S> {
    type Error = Infallible;

    #[inline]
    fn set_high(&mut self) -> Result<(), Self::Error> {
        self.set_high();
        Ok(())
    }

    #[inline]
    fn set_low(&mut self) -> Result<(), Self::Error> {
        self.set_low();
        Ok(())
    }
}

impl embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'_, SenseEnabled> {
    #[inline]
    fn is_set_high(&self) -> Result<bool, Self::Error> {
        Ok(self.is_set_high())
    }

    #[inline]
    fn is_set_low(&self) -> Result<bool, Self::Error> {
        Ok(self.is_set_low())
    }
}

impl<S: Sense> embedded_hal_02::digital::v2::ToggleableOutputPin for Flex<'_, S> {
    type Error = Infallible;

    #[inline]
    fn toggle(&mut self) -> Result<(), Self::Error> {
        self.toggle();
        Ok(())
    }
}

impl embedded_hal_02::digital::v2::InputPin for Input<'_> {
    type Error = Infallible;

    #[inline]
    fn is_high(&self) -> Result<bool, Self::Error> {
        Ok(self.is_high())
    }

    #[inline]
    fn is_low(&self) -> Result<bool, Self::Error> {
        Ok(self.is_low())
    }
}

impl embedded_hal_02::digital::v2::OutputPin for Output<'_> {
    type Error = Infallible;

    #[inline]
    fn set_high(&mut self) -> Result<(), Self::Error> {
        self.set_high();
        Ok(())
    }

    #[inline]
    fn set_low(&mut self) -> Result<(), Self::Error> {
        self.set_low();
        Ok(())
    }
}

impl embedded_hal_02::digital::v2::StatefulOutputPin for Output<'_> {
    #[inline]
    fn is_set_high(&self) -> Result<bool, Self::Error> {
        Ok(self.is_set_high())
    }

    #[inline]
    fn is_set_low(&self) -> Result<bool, Self::Error> {
        Ok(self.is_set_low())
    }
}

impl embedded_hal_02::digital::v2::ToggleableOutputPin for Output<'_> {
    type Error = Infallible;

    #[inline]
    fn toggle(&mut self) -> Result<(), Self::Error> {
        self.toggle();
        Ok(())
    }
}

impl<S: Sense> embedded_hal_1::digital::ErrorType for Flex<'_, S> {
    type Error = Infallible;
}

impl embedded_hal_1::digital::InputPin for Flex<'_, SenseEnabled> {
    #[inline]
    fn is_high(&mut self) -> Result<bool, Self::Error> {
        // Dereference of self is used here and a few other places to
        // access the correct method (since different types/traits
        // share method names)
        Ok((*self).is_high())
    }

    #[inline]
    fn is_low(&mut self) -> Result<bool, Self::Error> {
        Ok((*self).is_low())
    }
}

impl<S: Sense> embedded_hal_1::digital::OutputPin for Flex<'_, S> {
    #[inline]
    fn set_high(&mut self) -> Result<(), Self::Error> {
        self.set_high();
        Ok(())
    }

    #[inline]
    fn set_low(&mut self) -> Result<(), Self::Error> {
        self.set_low();
        Ok(())
    }
}

impl embedded_hal_1::digital::StatefulOutputPin for Flex<'_, SenseEnabled> {
    #[inline]
    fn is_set_high(&mut self) -> Result<bool, Self::Error> {
        Ok((*self).is_set_high())
    }

    #[inline]
    fn is_set_low(&mut self) -> Result<bool, Self::Error> {
        Ok((*self).is_set_low())
    }
}

impl<'d> embedded_hal_async::digital::Wait for Flex<'d, SenseEnabled> {
    #[inline]
    async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
        self.wait_for_high().await;
        Ok(())
    }

    #[inline]
    async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
        self.wait_for_low().await;
        Ok(())
    }

    #[inline]
    async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
        self.wait_for_rising_edge().await;
        Ok(())
    }

    #[inline]
    async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
        self.wait_for_falling_edge().await;
        Ok(())
    }

    #[inline]
    async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
        self.wait_for_any_edge().await;
        Ok(())
    }
}

impl embedded_hal_1::digital::ErrorType for Input<'_> {
    type Error = Infallible;
}

impl embedded_hal_1::digital::InputPin for Input<'_> {
    #[inline]
    fn is_high(&mut self) -> Result<bool, Self::Error> {
        Ok((*self).is_high())
    }

    #[inline]
    fn is_low(&mut self) -> Result<bool, Self::Error> {
        Ok((*self).is_low())
    }
}

impl<'d> embedded_hal_async::digital::Wait for Input<'d> {
    #[inline]
    async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
        self.wait_for_high().await;
        Ok(())
    }

    #[inline]
    async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
        self.wait_for_low().await;
        Ok(())
    }

    #[inline]
    async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
        self.wait_for_rising_edge().await;
        Ok(())
    }

    #[inline]
    async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
        self.wait_for_falling_edge().await;
        Ok(())
    }

    #[inline]
    async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
        self.wait_for_any_edge().await;
        Ok(())
    }
}

impl embedded_hal_1::digital::ErrorType for Output<'_> {
    type Error = Infallible;
}

impl embedded_hal_1::digital::OutputPin for Output<'_> {
    #[inline]
    fn set_high(&mut self) -> Result<(), Self::Error> {
        self.set_high();
        Ok(())
    }

    #[inline]
    fn set_low(&mut self) -> Result<(), Self::Error> {
        self.set_low();
        Ok(())
    }
}

impl embedded_hal_1::digital::StatefulOutputPin for Output<'_> {
    #[inline]
    fn is_set_high(&mut self) -> Result<bool, Self::Error> {
        Ok((*self).is_set_high())
    }

    #[inline]
    fn is_set_low(&mut self) -> Result<bool, Self::Error> {
        Ok((*self).is_set_low())
    }
}
