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

use stm32f4xx_hal::gpio::gpioa::*;
use stm32f4xx_hal::gpio::gpiob::*;
use stm32f4xx_hal::gpio::gpioc::*;
use stm32f4xx_hal::gpio::gpiod::*;
use stm32f4xx_hal::gpio::gpioe::*;
use stm32f4xx_hal::gpio::gpiof::*;
use stm32f4xx_hal::gpio::gpiog::*;
use stm32f4xx_hal::gpio::*;
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: PC0<Output<OpenDrain>>,
    vol1: PC1<Output<OpenDrain>>,
    vol2: PA0<Output<OpenDrain>>,
    vol3: PA1<Output<OpenDrain>>,
    vol4: PA2<Output<OpenDrain>>,
    vol5: PA3<Output<OpenDrain>>,
    vol6: PC5<Output<OpenDrain>>,
    vol7: PB0<Output<OpenDrain>>,
    vol8: PB1<Output<OpenDrain>>,
    vol9: PF11<Output<OpenDrain>>,
    vol10: PF12<Output<OpenDrain>>,
    vol11: PF13<Output<OpenDrain>>,
    vol12: PF14<Output<OpenDrain>>,
    vol13: PF15<Output<OpenDrain>>,
    vol14: PG0<Output<OpenDrain>>,
    vol15: PG1<Output<OpenDrain>>,

    front_rst: PA4<Output<PushPull>>,
    front_sclk: PA5<Alternate<AF5>>,
    front_mosi: PA6<Alternate<AF5>>,
    front_rclk: PA7<Output<PushPull>>,
}

/// Connection to the bluetooth module.
pub struct BluetoothModulePins {
    mfb: PG10<Output<PushPull>>,
    p6: PG12<Output<PushPull>>,
    p7: PG13<Output<PushPull>>,
    p8: PG14<Input<Floating>>,
    p9: PD7<Input<Floating>>,
    p16: PD6<Output<PushPull>>,
    p17: PD5<Output<PushPull>>,
    p18: PD4<Output<PushPull>>,
    p21: PD3<Output<PushPull>>,
    p29: PD2<Input<Floating>>,
    p30: PD1<Input<Floating>>,
    p31: PD0<Input<Floating>>,

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

    spi_pcm: PG11<Output<PushPull>>,
    i2s_ws: PA15<Alternate<AF6>>,
    i2s_ck: PC10<Alternate<AF6>>,
    i2s_sdi: PC11<Alternate<AF5>>,
    i2s_sdo: PC12<Alternate<AF6>>,
}

/// Front panel input pins.
pub struct InputPins {
    ir: PC4<Input<Floating>>,
    hp_detect: PC13<Input<Floating>>,
    pwr: PF2<Input<Floating>>,
    mic1: PF3<Input<Floating>>,
    mic2: PF4<Input<Floating>>,
    bt1: PF5<Input<Floating>>,
    bt2: PF6<Input<Floating>>,
    enc_a: PE7<Input<Floating>>,
    enc_b: PE8<Input<Floating>>,
    enc_sw: PE9<Input<Floating>>,
}
/// Connection to the amplifier ICs.
pub struct AmplifierPins {
    spk_fault: PE0<Input<Floating>>,
    spk_shutdown: PE1<Output<PushPull>>,

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

    i2s_mck: PE2<Alternate<AF5>>,
    i2s_ws: PE4<Alternate<AF5>>,
    i2s_ck: PE5<Alternate<AF5>>,
    i2s_sdi: PE6<Alternate<AF6>>,
    i2s_sdo: PE3<Alternate<AF5>>,
}

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

/// Extension header pins.
pub struct ExtensionPins {
    amp_ready: PE10<Output<PushPull>>,
    ext_ready: PE15<Input<Floating>>,

    spi_ss: PE11<Alternate<AF5>>,
    spi_sck: PE12<Alternate<AF5>>,
    spi_miso: PE13<Alternate<AF5>>,
    spi_mosi: PE14<Alternate<AF5>>,

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


/// 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: gpioc.pc0.into_open_drain_output(),
            vol1: gpioc.pc1.into_open_drain_output(),
            vol2: gpioa.pa0.into_open_drain_output(),
            vol3: gpioa.pa1.into_open_drain_output(),
            vol4: gpioa.pa2.into_open_drain_output(),
            vol5: gpioa.pa3.into_open_drain_output(),
            vol6: gpioc.pc5.into_open_drain_output(),
            vol7: gpiob.pb0.into_open_drain_output(),
            vol8: gpiob.pb1.into_open_drain_output(),
            vol9: gpiof.pf11.into_open_drain_output(),
            vol10: gpiof.pf12.into_open_drain_output(),
            vol11: gpiof.pf13.into_open_drain_output(),
            vol12: gpiof.pf14.into_open_drain_output(),
            vol13: gpiof.pf15.into_open_drain_output(),
            vol14: gpiog.pg0.into_open_drain_output(),
            vol15: gpiog.pg1.into_open_drain_output(),

            front_rst: gpioa.pa4.into_push_pull_output(),
            front_sclk: gpioa.pa5.into_alternate_af5(),
            front_mosi: gpioa.pa6.into_alternate_af5(),
            front_rclk: gpioa.pa7.into_push_pull_output(),
        };
        // TODO: Initial LED values.

        let bluetooth_module = BluetoothModulePins {
            mfb: gpiog.pg10.into_push_pull_output(),
            p6: gpiog.pg12.into_push_pull_output(),
            p7: gpiog.pg13.into_push_pull_output(),
            p8: gpiog.pg14.into_floating_input(),
            p9: gpiod.pd7.into_floating_input(),
            p16: gpiod.pd6.into_push_pull_output(),
            p17: gpiod.pd5.into_push_pull_output(),
            p18: gpiod.pd4.into_push_pull_output(),
            p21: gpiod.pd3.into_push_pull_output(),
            p29: gpiod.pd2.into_floating_input(),
            p30: gpiod.pd1.into_floating_input(),
            p31: gpiod.pd0.into_floating_input(),

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

            spi_pcm: gpiog.pg11.into_push_pull_output(),
            i2s_ws: gpioa.pa15.into_alternate_af6(),
            i2s_ck: gpioc.pc10.into_alternate_af6(),
            i2s_sdi: gpioc.pc11.into_alternate_af5(),
            i2s_sdo: gpioc.pc12.into_alternate_af6(),
        };

        let input = InputPins {
            ir: gpioc.pc4.into_floating_input(),
            hp_detect: gpioc.pc13.into_floating_input(),
            pwr: gpiof.pf2.into_floating_input(),
            mic1: gpiof.pf3.into_floating_input(),
            mic2: gpiof.pf4.into_floating_input(),
            bt1: gpiof.pf5.into_floating_input(),
            bt2: gpiof.pf6.into_floating_input(),
            enc_a: gpioe.pe7.into_floating_input(),
            enc_b: gpioe.pe8.into_floating_input(),
            enc_sw: gpioe.pe9.into_floating_input(),
        };

        let amplifier = AmplifierPins {
            spk_fault: gpioe.pe0.into_floating_input(),
            spk_shutdown: gpioe.pe1.into_push_pull_output(),

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

            i2s_mck: gpioe.pe2.into_alternate_af5(),
            i2s_ws: gpioe.pe4.into_alternate_af5(),
            i2s_ck: gpioe.pe5.into_alternate_af5(),
            i2s_sdi: gpioe.pe6.into_alternate_af6(),
            i2s_sdo: gpioe.pe3.into_alternate_af5(),
        };

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

        /*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,
        }
    }
}
