//! GPIO pin configuration.
#![warn(missing_docs)]
#![allow(unused)] // TODO: Enable this warning again.

use stm32f4xx_hal::gpio::gpioa::{PA0, PA1, PA10, PA11, PA12, PA15, PA2, PA3, PA4, PA5, PA6, PA7};
use stm32f4xx_hal::gpio::gpiob::{PB10, PB13, PB14, PB15, PB3, PB4, PB5, PB6, PB7, PB9};
use stm32f4xx_hal::gpio::gpioc::{PC0, PC1, PC15, PC2, PC3, PC6};
use stm32f4xx_hal::gpio::gpiod::{PD0, PD1, PD10, PD11, PD12, PD13, PD14, PD15, PD7, PD8, PD9};
use stm32f4xx_hal::gpio::gpioe::{PE3, PE4, PE5, PE6};
use stm32f4xx_hal::gpio::gpiof::{
    PF0, PF1, PF10, PF13, PF14, PF15, PF2, PF3, PF4, PF5, PF6, PF7, PF8, PF9,
};
use stm32f4xx_hal::gpio::gpiog::{PG10, PG11, PG13, PG14, PG15, PG2, PG3, PG4, PG5, PG6};
use stm32f4xx_hal::gpio::{
    Alternate, Floating, Input, OpenDrain, Output, PushPull, AF10, AF12, AF4, AF5, AF6, AF7,
};
use stm32f4xx_hal::prelude::*;
use stm32f4xx_hal::stm32::{DMA1, GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, GPIOF, GPIOG, I2S2EXT, SPI2};

use super::halext::i2s::I2SClocks;

/// Front panel LEDs.
pub struct LedPins {
    vol0: PF2<Output<OpenDrain>>,
    vol1: PF3<Output<OpenDrain>>,
    vol2: PF4<Output<OpenDrain>>,
    vol3: PF5<Output<OpenDrain>>,
    vol4: PF6<Output<OpenDrain>>,
    vol5: PF7<Output<OpenDrain>>,
    vol6: PF8<Output<OpenDrain>>,
    vol7: PF9<Output<OpenDrain>>,
    vol8: PF10<Output<OpenDrain>>,
    vol9: PC0<Output<OpenDrain>>,
    vol10: PC1<Output<OpenDrain>>,
    vol11: PA0<Output<OpenDrain>>,
    vol12: PA1<Output<OpenDrain>>,
    vol13: PA2<Output<OpenDrain>>,
    vol14: PA3<Output<OpenDrain>>,
    vol15: PA4<Output<OpenDrain>>,

    front_rst: PG11<Output<PushPull>>,
    front_sclk: PG13<Output<PushPull>>,
    front_mosi: PG14<Output<PushPull>>,
    front_rclk: PG15<Output<PushPull>>,
}

/// Connection to the bluetooth module.
pub struct BluetoothModulePins {
    mfb: PG2<Output<PushPull>>,
    p6: PG4<Output<PushPull>>,
    p7: PG5<Output<PushPull>>,
    p8: PG6<Input<Floating>>,
    p9: PD15<Input<Floating>>,
    p16: PD14<Output<PushPull>>,
    p17: PD13<Output<PushPull>>,
    p18: PD12<Output<PushPull>>,
    p21: PD11<Output<PushPull>>,
    p29: PD10<Input<Floating>>,
    p30: PD9<Input<Floating>>,
    p31: PD8<Input<Floating>>,

    sda: PF0<Alternate<AF4>>,
    scl: PF1<Alternate<AF4>>,

    spi_pcm: PG3<Output<PushPull>>,
    i2s_ws: PA15<Alternate<AF6>>,
    i2s_ck: PB3<Alternate<AF6>>,
    i2s_sdi: PB4<Alternate<AF7>>,
    i2s_sdo: PB5<Alternate<AF6>>,
}

/// Front panel input pins.
pub struct InputPins {
    ir: PG10<Input<Floating>>,
    hp_detect: PD7<Input<Floating>>,
    pwr: PE3<Input<Floating>>,
    mic1: PE4<Input<Floating>>,
    mic2: PE5<Input<Floating>>,
    bt1: PE6<Input<Floating>>,
    bt2: PC15<Input<Floating>>,
    enc_a: PA5<Input<Floating>>,
    enc_b: PA6<Input<Floating>>,
    enc_sw: PA7<Input<Floating>>,
}
/// Connection to the amplifier ICs.
pub struct AmplifierPins {
    spk_fault: PD0<Input<Floating>>,
    spk_shutdown: PD1<Output<PushPull>>,

    sda: PB7<Alternate<AF4>>,
    scl: PB6<Alternate<AF4>>,

    i2s_mck: PC6<Alternate<AF5>>,
    i2s_ws: PB9<Alternate<AF5>>,
    i2s_ck: PB10<Alternate<AF5>>,
    i2s_sdi: PC2<Alternate<AF6>>,
    i2s_sdo: PC3<Alternate<AF5>>,
}

/// USB pins.
pub struct UsbPins {
    usb1_detect: PA10<Input<Floating>>,
    usb1_dm: PA11<Alternate<AF10>>,
    usb1_dp: PA12<Alternate<AF10>>,
    usb2_detect: PB13<Input<Floating>>,
    usb2_dm: PB14<Alternate<AF12>>,
    usb2_dp: PB15<Alternate<AF12>>,
}

/// Type which contains all GPIO pins.
///
/// The pins are configured as the correct type. Whenever possible, the corresponding HAL driver is
/// instantiated. The initial output state is set during configuration.
pub struct Pins {
    leds: LedPins,
    bluetooth_module: BluetoothModulePins,
    input: InputPins,
    amplifier: AmplifierPins,
    usb: UsbPins,
}

impl Pins {
    /// Configures the GPIO pins and sets their initial value.
    pub fn configure(
        gpioa: GPIOA,
        gpiob: GPIOB,
        gpioc: GPIOC,
        gpiod: GPIOD,
        gpioe: GPIOE,
        gpiof: GPIOF,
        gpiog: GPIOG,
        dma1: DMA1,
        spi2: SPI2,
        i2s2ext: I2S2EXT,
        i2sclocks: I2SClocks,
    ) -> Pins {
        let gpioa = gpioa.split();
        let gpiob = gpiob.split();
        let gpioc = gpioc.split();
        let gpiod = gpiod.split();
        let gpioe = gpioe.split();
        let gpiof = gpiof.split();
        let gpiog = gpiog.split();

        let leds = LedPins {
            vol0: gpiof.pf2.into_open_drain_output(),
            vol1: gpiof.pf3.into_open_drain_output(),
            vol2: gpiof.pf4.into_open_drain_output(),
            vol3: gpiof.pf5.into_open_drain_output(),
            vol4: gpiof.pf6.into_open_drain_output(),
            vol5: gpiof.pf7.into_open_drain_output(),
            vol6: gpiof.pf8.into_open_drain_output(),
            vol7: gpiof.pf9.into_open_drain_output(),
            vol8: gpiof.pf10.into_open_drain_output(),
            vol9: gpioc.pc0.into_open_drain_output(),
            vol10: gpioc.pc1.into_open_drain_output(),
            vol11: gpioa.pa0.into_open_drain_output(),
            vol12: gpioa.pa1.into_open_drain_output(),
            vol13: gpioa.pa2.into_open_drain_output(),
            vol14: gpioa.pa3.into_open_drain_output(),
            vol15: gpioa.pa4.into_open_drain_output(),

            front_rst: gpiog.pg11.into_push_pull_output(),
            front_sclk: gpiog.pg13.into_push_pull_output(),
            front_mosi: gpiog.pg14.into_push_pull_output(),
            front_rclk: gpiog.pg15.into_push_pull_output(),
        };
        // TODO: Initial LED values.

        let bluetooth_module = BluetoothModulePins {
            mfb: gpiog.pg2.into_push_pull_output(),
            p6: gpiog.pg4.into_push_pull_output(),
            p7: gpiog.pg5.into_push_pull_output(),
            p8: gpiog.pg6.into_floating_input(),
            p9: gpiod.pd15.into_floating_input(),
            p16: gpiod.pd14.into_push_pull_output(),
            p17: gpiod.pd13.into_push_pull_output(),
            p18: gpiod.pd12.into_push_pull_output(),
            p21: gpiod.pd11.into_push_pull_output(),
            p29: gpiod.pd10.into_floating_input(),
            p30: gpiod.pd9.into_floating_input(),
            p31: gpiod.pd8.into_floating_input(),

            sda: gpiof.pf0.into_alternate_af4(),
            scl: gpiof.pf1.into_alternate_af4(),

            spi_pcm: gpiog.pg3.into_push_pull_output(),
            i2s_ws: gpioa.pa15.into_alternate_af6(),
            i2s_ck: gpiob.pb3.into_alternate_af6(),
            i2s_sdi: gpiob.pb4.into_alternate_af7(),
            i2s_sdo: gpiob.pb5.into_alternate_af6(),
        };

        let input = InputPins {
            ir: gpiog.pg10.into_floating_input(),
            hp_detect: gpiod.pd7.into_floating_input(),
            pwr: gpioe.pe3.into_floating_input(),
            mic1: gpioe.pe4.into_floating_input(),
            mic2: gpioe.pe5.into_floating_input(),
            bt1: gpioe.pe6.into_floating_input(),
            bt2: gpioc.pc15.into_floating_input(),
            enc_a: gpioa.pa5.into_floating_input(),
            enc_b: gpioa.pa6.into_floating_input(),
            enc_sw: gpioa.pa7.into_floating_input(),
        };

        let amplifier = AmplifierPins {
            spk_fault: gpiod.pd0.into_floating_input(),
            spk_shutdown: gpiod.pd1.into_push_pull_output(),

            sda: gpiob.pb7.into_alternate_af4(),
            scl: gpiob.pb6.into_alternate_af4(),

            i2s_mck: gpioc.pc6.into_alternate_af5(),
            i2s_ws: gpiob.pb9.into_alternate_af5(),
            i2s_ck: gpiob.pb10.into_alternate_af5(),
            i2s_sdi: gpioc.pc2.into_alternate_af6(),
            i2s_sdo: gpioc.pc3.into_alternate_af5(),
        };

        let usb = UsbPins {
            usb1_detect: gpioa.pa10.into_floating_input(),
            usb1_dm: gpioa.pa11.into_alternate_af10(),
            usb1_dp: gpioa.pa12.into_alternate_af10(),
            usb2_detect: gpiob.pb13.into_floating_input(),
            usb2_dm: gpiob.pb14.into_alternate_af12(),
            usb2_dp: gpiob.pb15.into_alternate_af12(),
        };

        /*let dma = StreamsTuple::new(dma1);
        let mut i2s = I2S::new(
            spi2,
            i2s2ext,
            dma.3,
            dma.4,
            gpioc.pc6.into_alternate_af5(),
            gpiob.pb12.into_alternate_af5(),
            gpiob.pb13.into_alternate_af5(),
            gpiob.pb14.into_alternate_af6(),
            gpiob.pb15.into_alternate_af5(),
            &AMP_I2S_IN_BUFFER,
            &AMP_I2S_OUT_BUFFER,
            i2sclocks,
        );*/

        Pins {
            leds,
            bluetooth_module,
            input,
            amplifier,
            usb,
        }
    }
}
