use core::ops::Deref;
use core::ptr;
pub use crate::hal::spi::{FullDuplex, Mode, Phase, Polarity};
#[cfg(any(feature = "high", feature = "connectivity"))]
use crate::pac::SPI3;
use crate::pac::{SPI1, SPI2};
use crate::afio::MAPR;
use crate::dma::dma1::{C3, C5};
#[cfg(feature = "connectivity")]
use crate::dma::dma2::C2;
use crate::dma::{Transfer, TransferPayload, Transmit, TxDma, R};
use crate::gpio::gpioa::{PA5, PA6, PA7};
use crate::gpio::gpiob::{PB13, PB14, PB15, PB3, PB4, PB5};
#[cfg(feature = "connectivity")]
use crate::gpio::gpioc::{PC10, PC11, PC12};
use crate::gpio::{Alternate, Floating, Input, PushPull};
use crate::rcc::{Clocks, Enable, GetBusFreq, Reset, APB1, APB2};
use crate::time::Hertz;
use core::sync::atomic::{self, Ordering};
use embedded_dma::StaticReadBuffer;
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
    
    Overrun,
    
    ModeFault,
    
    Crc,
}
use core::marker::PhantomData;
mod sealed {
    pub trait Remap {
        type Periph;
        const REMAP: bool;
    }
    pub trait Sck<REMAP> {}
    pub trait Miso<REMAP> {}
    pub trait Mosi<REMAP> {}
    pub struct _Sck;
    pub struct _Miso;
    pub struct _Mosi;
}
use sealed::{Miso, Mosi, Remap, Sck};
pub trait Pins<REMAP, P> {
    type _Pos;
}
macro_rules! pins_impl {
    ( $( ( $($PINX:ident),+ ), ( $($TRAIT:ident),+ ), ( $($POS:ident),* ); )+ ) => {
        $(
            #[allow(unused_parens)]
            impl<REMAP, $($PINX,)+> Pins<REMAP, ($(sealed::$POS),+)> for ($($PINX),+)
            where
                $($PINX: $TRAIT<REMAP>,)+
            {
                type _Pos = ($(sealed::$POS),+);
            }
        )+
    };
}
pins_impl!(
    (SCK, MISO, MOSI), (Sck, Miso, Mosi), (_Sck, _Miso, _Mosi);
    (SCK, MOSI, MISO), (Sck, Mosi, Miso), (_Sck, _Mosi, _Miso);
    (MOSI, SCK, MISO), (Mosi, Sck, Miso), (_Mosi, _Sck, _Miso);
    (MOSI, MISO, SCK), (Mosi, Miso, Sck), (_Mosi, _Miso, _Sck);
    (MISO, MOSI, SCK), (Miso, Mosi, Sck), (_Miso, _Mosi, _Sck);
    (MISO, SCK, MOSI), (Miso, Sck, Mosi), (_Miso, _Sck, _Mosi);
);
pub struct Spi<SPI, REMAP, PINS, FRAMESIZE> {
    spi: SPI,
    pins: PINS,
    _remap: PhantomData<REMAP>,
    _framesize: PhantomData<FRAMESIZE>,
}
pub struct NoSck;
pub struct NoMiso;
pub struct NoMosi;
impl<REMAP> Sck<REMAP> for NoSck {}
impl<REMAP> Miso<REMAP> for NoMiso {}
impl<REMAP> Mosi<REMAP> for NoMosi {}
macro_rules! remap {
    ($name:ident, $SPIX:ident, $state:literal, $SCK:ident, $MISO:ident, $MOSI:ident) => {
        pub struct $name;
        impl Remap for $name {
            type Periph = $SPIX;
            const REMAP: bool = $state;
        }
        impl Sck<$name> for $SCK<Alternate<PushPull>> {}
        impl Miso<$name> for $MISO<Input<Floating>> {}
        impl Mosi<$name> for $MOSI<Alternate<PushPull>> {}
    };
}
remap!(Spi1NoRemap, SPI1, false, PA5, PA6, PA7);
remap!(Spi1Remap, SPI1, true, PB3, PB4, PB5);
remap!(Spi2NoRemap, SPI2, false, PB13, PB14, PB15);
#[cfg(feature = "high")]
remap!(Spi3NoRemap, SPI3, false, PB3, PB4, PB5);
#[cfg(feature = "connectivity")]
remap!(Spi3Remap, SPI3, true, PC10, PC11, PC12);
impl<REMAP, PINS> Spi<SPI1, REMAP, PINS, u8> {
    
    pub fn spi1<F, POS>(
        spi: SPI1,
        pins: PINS,
        mapr: &mut MAPR,
        mode: Mode,
        freq: F,
        clocks: Clocks,
        apb: &mut APB2,
    ) -> Self
    where
        F: Into<Hertz>,
        REMAP: Remap<Periph = SPI1>,
        PINS: Pins<REMAP, POS>,
    {
        mapr.modify_mapr(|_, w| w.spi1_remap().bit(REMAP::REMAP));
        Spi::<SPI1, _, _, u8>::_spi(spi, pins, mode, freq.into(), clocks, apb)
    }
}
impl<REMAP, PINS> Spi<SPI2, REMAP, PINS, u8> {
    
    pub fn spi2<F, POS>(
        spi: SPI2,
        pins: PINS,
        mode: Mode,
        freq: F,
        clocks: Clocks,
        apb: &mut APB1,
    ) -> Self
    where
        F: Into<Hertz>,
        REMAP: Remap<Periph = SPI2>,
        PINS: Pins<REMAP, POS>,
    {
        Spi::<SPI2, _, _, u8>::_spi(spi, pins, mode, freq.into(), clocks, apb)
    }
}
#[cfg(any(feature = "high", feature = "connectivity"))]
impl<REMAP, PINS> Spi<SPI3, REMAP, PINS, u8> {
    
    pub fn spi3<F, POS>(
        spi: SPI3,
        pins: PINS,
        mapr: &mut MAPR,
        mode: Mode,
        freq: F,
        clocks: Clocks,
        apb: &mut APB1,
    ) -> Self
    where
        F: Into<Hertz>,
        REMAP: Remap<Periph = SPI3>,
        PINS: Pins<REMAP, POS>,
    {
        mapr.modify_mapr(|_, w| w.spi3_remap().bit(REMAP::REMAP));
        Spi::<SPI3, _, _, u8>::_spi(spi, pins, mode, freq.into(), clocks, apb)
    }
}
pub type SpiRegisterBlock = crate::pac::spi1::RegisterBlock;
pub trait SpiReadWrite<T> {
    fn read_data_reg(&mut self) -> T;
    fn write_data_reg(&mut self, data: T);
    fn spi_write(&mut self, words: &[T]) -> Result<(), Error>;
}
impl<SPI, REMAP, PINS, FrameSize> SpiReadWrite<FrameSize> for Spi<SPI, REMAP, PINS, FrameSize>
where
    SPI: Deref<Target = SpiRegisterBlock>,
    FrameSize: Copy,
{
    fn read_data_reg(&mut self) -> FrameSize {
        
        
        unsafe { ptr::read_volatile(&self.spi.dr as *const _ as *const FrameSize) }
    }
    fn write_data_reg(&mut self, data: FrameSize) {
        
        unsafe { ptr::write_volatile(&self.spi.dr as *const _ as *mut FrameSize, data) }
    }
    
    
    
    
    fn spi_write(&mut self, words: &[FrameSize]) -> Result<(), Error> {
        
        for word in words {
            loop {
                let sr = self.spi.sr.read();
                if sr.txe().bit_is_set() {
                    
                    
                    self.write_data_reg(*word);
                    if sr.modf().bit_is_set() {
                        return Err(Error::ModeFault);
                    }
                    break;
                }
            }
        }
        
        loop {
            let sr = self.spi.sr.read();
            if sr.txe().bit_is_set() {
                break;
            }
        }
        
        loop {
            let sr = self.spi.sr.read();
            if !sr.bsy().bit_is_set() {
                break;
            }
        }
        
        
        
        
        
        let _ = self.read_data_reg();
        let _ = self.spi.sr.read();
        Ok(())
    }
}
impl<SPI, REMAP, PINS, FrameSize> Spi<SPI, REMAP, PINS, FrameSize>
where
    SPI: Deref<Target = SpiRegisterBlock>,
    FrameSize: Copy,
{
    #[deprecated(since = "0.6.0", note = "Please use release instead")]
    pub fn free(self) -> (SPI, PINS) {
        self.release()
    }
    pub fn release(self) -> (SPI, PINS) {
        (self.spi, self.pins)
    }
}
impl<SPI, REMAP, PINS> Spi<SPI, REMAP, PINS, u8>
where
    SPI: Deref<Target = SpiRegisterBlock> + Enable + Reset,
    SPI::Bus: GetBusFreq,
{
    fn _spi(
        spi: SPI,
        pins: PINS,
        mode: Mode,
        freq: Hertz,
        clocks: Clocks,
        apb: &mut SPI::Bus,
    ) -> Self {
        
        SPI::enable(apb);
        SPI::reset(apb);
        
        spi.cr2.write(|w| w.ssoe().clear_bit());
        let br = match SPI::Bus::get_frequency(&clocks).0 / freq.0 {
            0 => unreachable!(),
            1..=2 => 0b000,
            3..=5 => 0b001,
            6..=11 => 0b010,
            12..=23 => 0b011,
            24..=47 => 0b100,
            48..=95 => 0b101,
            96..=191 => 0b110,
            _ => 0b111,
        };
        spi.cr1.write(|w| {
            w
                
                .cpha()
                .bit(mode.phase == Phase::CaptureOnSecondTransition)
                
                .cpol()
                .bit(mode.polarity == Polarity::IdleHigh)
                
                .mstr()
                .set_bit()
                
                .br()
                .bits(br)
                
                .lsbfirst()
                .clear_bit()
                
                .ssm()
                .set_bit()
                
                .ssi()
                .set_bit()
                
                .dff()
                .clear_bit()
                
                .bidimode()
                .clear_bit()
                
                .rxonly()
                .clear_bit()
                
                .spe()
                .set_bit()
        });
        Spi {
            spi,
            pins,
            _remap: PhantomData,
            _framesize: PhantomData,
        }
    }
    
    pub fn frame_size_16bit(self) -> Spi<SPI, REMAP, PINS, u16> {
        self.spi.cr1.modify(|_, w| w.spe().clear_bit());
        self.spi.cr1.modify(|_, w| w.dff().set_bit());
        self.spi.cr1.modify(|_, w| w.spe().set_bit());
        Spi {
            spi: self.spi,
            pins: self.pins,
            _remap: PhantomData,
            _framesize: PhantomData,
        }
    }
}
impl<SPI, REMAP, PINS> Spi<SPI, REMAP, PINS, u16>
where
    SPI: Deref<Target = SpiRegisterBlock>,
{
    
    pub fn frame_size_8bit(self) -> Spi<SPI, REMAP, PINS, u8> {
        self.spi.cr1.modify(|_, w| w.spe().clear_bit());
        self.spi.cr1.modify(|_, w| w.dff().clear_bit());
        self.spi.cr1.modify(|_, w| w.spe().set_bit());
        Spi {
            spi: self.spi,
            pins: self.pins,
            _remap: PhantomData,
            _framesize: PhantomData,
        }
    }
}
impl<SPI, REMAP, PINS, FrameSize> crate::hal::spi::FullDuplex<FrameSize>
    for Spi<SPI, REMAP, PINS, FrameSize>
where
    SPI: Deref<Target = SpiRegisterBlock>,
    FrameSize: Copy,
{
    type Error = Error;
    fn read(&mut self) -> nb::Result<FrameSize, Error> {
        let sr = self.spi.sr.read();
        Err(if sr.ovr().bit_is_set() {
            nb::Error::Other(Error::Overrun)
        } else if sr.modf().bit_is_set() {
            nb::Error::Other(Error::ModeFault)
        } else if sr.crcerr().bit_is_set() {
            nb::Error::Other(Error::Crc)
        } else if sr.rxne().bit_is_set() {
            
            
            return Ok(self.read_data_reg());
        } else {
            nb::Error::WouldBlock
        })
    }
    fn send(&mut self, data: FrameSize) -> nb::Result<(), Error> {
        let sr = self.spi.sr.read();
        Err(if sr.ovr().bit_is_set() {
            nb::Error::Other(Error::Overrun)
        } else if sr.modf().bit_is_set() {
            nb::Error::Other(Error::ModeFault)
        } else if sr.crcerr().bit_is_set() {
            nb::Error::Other(Error::Crc)
        } else if sr.txe().bit_is_set() {
            
            self.write_data_reg(data);
            return Ok(());
        } else {
            nb::Error::WouldBlock
        })
    }
}
impl<SPI, REMAP, PINS, FrameSize> crate::hal::blocking::spi::transfer::Default<FrameSize>
    for Spi<SPI, REMAP, PINS, FrameSize>
where
    SPI: Deref<Target = SpiRegisterBlock>,
    FrameSize: Copy,
{
}
impl<SPI, REMAP, PINS> crate::hal::blocking::spi::Write<u8> for Spi<SPI, REMAP, PINS, u8>
where
    SPI: Deref<Target = SpiRegisterBlock>,
{
    type Error = Error;
    
    
    
    
    fn write(&mut self, words: &[u8]) -> Result<(), Error> {
        self.spi_write(words)
    }
}
impl<SPI, REMAP, PINS> crate::hal::blocking::spi::Write<u16> for Spi<SPI, REMAP, PINS, u16>
where
    SPI: Deref<Target = SpiRegisterBlock>,
{
    type Error = Error;
    fn write(&mut self, words: &[u16]) -> Result<(), Error> {
        self.spi_write(words)
    }
}
pub struct SpiPayload<SPI, REMAP, PINS> {
    spi: Spi<SPI, REMAP, PINS, u8>,
}
pub type SpiTxDma<SPI, REMAP, PINS, CHANNEL> = TxDma<SpiPayload<SPI, REMAP, PINS>, CHANNEL>;
macro_rules! spi_dma {
    ($SPIi:ident, $TCi:ident) => {
        impl<REMAP, PINS> Transmit for SpiTxDma<$SPIi, REMAP, PINS, $TCi> {
            type TxChannel = $TCi;
            type ReceivedWord = u8;
        }
        impl<REMAP, PINS> Spi<$SPIi, REMAP, PINS, u8> {
            pub fn with_tx_dma(self, channel: $TCi) -> SpiTxDma<$SPIi, REMAP, PINS, $TCi> {
                let payload = SpiPayload { spi: self };
                SpiTxDma { payload, channel }
            }
        }
        impl<REMAP, PINS> TransferPayload for SpiTxDma<$SPIi, REMAP, PINS, $TCi> {
            fn start(&mut self) {
                self.payload
                    .spi
                    .spi
                    .cr2
                    .modify(|_, w| w.txdmaen().set_bit());
                self.channel.start();
            }
            fn stop(&mut self) {
                self.payload
                    .spi
                    .spi
                    .cr2
                    .modify(|_, w| w.txdmaen().clear_bit());
                self.channel.stop();
            }
        }
        impl<B, REMAP, PIN> crate::dma::WriteDma<B, u8> for SpiTxDma<$SPIi, REMAP, PIN, $TCi>
        where
            B: StaticReadBuffer<Word = u8>,
        {
            fn write(mut self, buffer: B) -> Transfer<R, B, Self> {
                
                
                let (ptr, len) = unsafe { buffer.static_read_buffer() };
                self.channel.set_peripheral_address(
                    unsafe { &(*$SPIi::ptr()).dr as *const _ as u32 },
                    false,
                );
                self.channel.set_memory_address(ptr as u32, true);
                self.channel.set_transfer_length(len);
                atomic::compiler_fence(Ordering::Release);
                self.channel.ch().cr.modify(|_, w| {
                    w
                        
                        .mem2mem()
                        .clear_bit()
                        
                        .pl()
                        .medium()
                        
                        .msize()
                        .bits8()
                        
                        .psize()
                        .bits8()
                        
                        .circ()
                        .clear_bit()
                        
                        .dir()
                        .set_bit()
                });
                self.start();
                Transfer::r(buffer, self)
            }
        }
    };
}
spi_dma!(SPI1, C3);
spi_dma!(SPI2, C5);
#[cfg(feature = "connectivity")]
spi_dma!(SPI3, C2);