#![allow(dead_code)]
use core::marker::PhantomData;
use core::ops;
use crate::rcc::AHB;
#[derive(Debug)]
pub enum Error {
    Overrun,
    #[doc(hidden)]
    _Extensible,
}
pub enum Event {
    HalfTransfer,
    TransferComplete,
}
#[derive(Clone, Copy, PartialEq)]
pub enum Half {
    First,
    Second,
}
pub struct CircBuffer<BUFFER, CHANNEL, RESOURCE>
    where
        BUFFER: 'static,
{
    buffer: &'static mut [BUFFER; 2],
    channel: CHANNEL,
    resource: RESOURCE,
    readable_half: Half,
}
impl<BUFFER, CHANNEL, RESOURCE> CircBuffer<BUFFER, CHANNEL, RESOURCE> {
    pub(crate) fn new(buf: &'static mut [BUFFER; 2], chan: CHANNEL, resource: RESOURCE) -> Self {
        CircBuffer {
            buffer: buf,
            channel: chan,
            resource,
            readable_half: Half::Second,
        }
    }
}
pub trait Static<B: ?Sized> {
    fn borrow(&self) -> &B;
}
impl<B: ?Sized> Static<B> for &'static B {
    fn borrow(&self) -> &B {
        *self
    }
}
impl<B: ?Sized> Static<B> for &'static mut B {
    fn borrow(&self) -> &B {
        *self
    }
}
pub trait DmaExt {
    type Channels;
    fn split(self, ahb: &mut AHB) -> Self::Channels;
}
pub struct Transfer<MODE, BUFFER, CHANNEL, PAYLOAD> {
    _mode: PhantomData<MODE>,
    buffer: BUFFER,
    channel: CHANNEL,
    payload: PAYLOAD,
}
impl<BUFFER, CHANNEL, PAYLOAD> Transfer<R, BUFFER, CHANNEL, PAYLOAD> {
    pub(crate) fn r(buffer: BUFFER, channel: CHANNEL, payload: PAYLOAD) -> Self {
        Transfer {
            _mode: PhantomData,
            buffer,
            channel,
            payload,
        }
    }
}
impl<BUFFER, CHANNEL, PAYLOAD> Transfer<W, BUFFER, CHANNEL, PAYLOAD> {
    pub(crate) fn w(buffer: BUFFER, channel: CHANNEL, payload: PAYLOAD) -> Self {
        Transfer {
            _mode: PhantomData,
            buffer,
            channel,
            payload,
        }
    }
}
impl<BUFFER, CHANNEL, PAYLOAD> ops::Deref for Transfer<R, BUFFER, CHANNEL, PAYLOAD> {
    type Target = BUFFER;
    fn deref(&self) -> &BUFFER {
        &self.buffer
    }
}
pub struct R;
pub struct W;
macro_rules! dma {
    ($($DMAX:ident: ($dmaX:ident, $dmaXen:ident, $dmaXrst:ident, {
        $($CX:ident: (
            $ccrX:ident,
            $CCRX:ident,
            $cndtrX:ident,
            $CNDTRX:ident,
            $cparX:ident,
            $CPARX:ident,
            $cmarX:ident,
            $CMARX:ident,
            $htifX:ident,
            $tcifX:ident,
            $chtifX:ident,
            $ctcifX:ident,
            $cgifX:ident
        ),)+
    }),)+) => {
        $(
            pub mod $dmaX {
                use core::sync::atomic::{self, Ordering};
                use crate::device::{$DMAX, dma1};
                use crate::dma::{CircBuffer, DmaExt, Error, Event, Half, Transfer, W};
                use crate::rcc::AHB;
                use crate::bb;
                pub struct Channels((), $(pub $CX),+);
                $(
                    pub struct $CX { _0: () }
                    impl $CX {
                        pub fn listen(&mut self, event: Event) {
                            match event {
                                Event::HalfTransfer => self.ccr().modify(|_, w| w.htie().set_bit()),
                                Event::TransferComplete => {
                                    self.ccr().modify(|_, w| w.tcie().set_bit())
                                }
                            }
                        }
                        pub fn unlisten(&mut self, event: Event) {
                            match event {
                                Event::HalfTransfer => {
                                    self.ccr().modify(|_, w| w.htie().clear_bit())
                                },
                                Event::TransferComplete => {
                                    self.ccr().modify(|_, w| w.tcie().clear_bit())
                                }
                            }
                        }
                        pub(crate) fn isr(&self) -> dma1::isr::R {
                            
                            unsafe { (*$DMAX::ptr()).isr.read() }
                        }
                        pub(crate) fn ifcr(&self) -> &dma1::IFCR {
                            unsafe { &(*$DMAX::ptr()).ifcr }
                        }
                        pub(crate) fn ccr(&mut self) -> &dma1::$CCRX {
                            unsafe { &(*$DMAX::ptr()).$ccrX }
                        }
                        pub(crate) fn cndtr(&mut self) -> &dma1::$CNDTRX {
                            unsafe { &(*$DMAX::ptr()).$cndtrX }
                        }
                        pub(crate) fn cpar(&mut self) -> &dma1::$CPARX {
                            unsafe { &(*$DMAX::ptr()).$cparX }
                        }
                        pub(crate) fn cmar(&mut self) -> &dma1::$CMARX {
                            unsafe { &(*$DMAX::ptr()).$cmarX }
                        }
                        pub(crate) fn get_cndtr(&self) -> u32 {
                            
                            unsafe { (*$DMAX::ptr()).$cndtrX.read().bits() }
                        }
                        fn tranfer_is_complete(&self) -> bool{
                            self.isr().$tcifX().bit_is_set()
                        }
                        pub(crate) fn wait_for_tranfer<B>(&mut self, buffer:B) ->B {
                        
                            
                            
                            
                            while !self.tranfer_is_complete() {}
                            self.ifcr().write(|w| w.$cgifX().set_bit());
                            bb::clear(self.ccr(), 0);
                            atomic::compiler_fence(Ordering::SeqCst);
                            buffer
                        }
                        pub fn do_complete_tranfer() {
                        }
                    }
                    impl<B: AsRef<[u8]> , RE> CircBuffer<B, $CX, RE> {
                        
                        pub fn peek_whole_buffer<R, F>(&mut self, f: F) -> Result<R, Error>
                            where F: FnOnce(&B, &B, usize) -> R,
                        {
                            let len = self.buffer[0].as_ref().len();
                            let written_counter = self.channel.get_cndtr() as usize;
                             
                            atomic::compiler_fence(Ordering::SeqCst);
                            let ret = f(&self.buffer[0], &self.buffer[1], len * 2 - written_counter);
                            Ok(ret)
                        }
                        
                        
                        pub fn retrieve_filled_half<R, F>(&mut self, f: F) -> Result<R, Error>
                            where
                            F: FnOnce(&B, Half, usize) -> R,
                        {
                            let half_being_read = self.get_filled_half()?;
                            let buf = match half_being_read {
                                Half::First => &self.buffer[0],
                                Half::Second => &self.buffer[1],
                            };
                            let len = buf.as_ref().len();
                            let written: usize = (len * 2 - self.channel.get_cndtr() as usize) % len;
                            
                            atomic::compiler_fence(Ordering::SeqCst);
                            let ret = f(buf, half_being_read, written);
                            let isr = self.channel.isr();
                            let first_half_is_done = isr.$htifX().bit_is_set();
                            let second_half_is_done = isr.$tcifX().bit_is_set();
                            if (half_being_read == Half::First && second_half_is_done) ||
                                (half_being_read == Half::Second && first_half_is_done) {
                                Err(Error::Overrun)
                            } else {
                                Ok(ret)
                            }
                        }
                        
                        
                        pub fn get_filled_half(&mut self) -> Result<Half, Error> {
                            let isr = self.channel.isr();
                            let first_half_is_done = isr.$htifX().bit_is_set();
                            let second_half_is_done = isr.$tcifX().bit_is_set();
                            if first_half_is_done && second_half_is_done {
                                return Err(Error::Overrun);
                            }
                            let last_read_half = self.readable_half;
                            Ok(match last_read_half {
                                Half::First => {
                                    if second_half_is_done {
                                        self.channel.ifcr().write(|w| w.$ctcifX().set_bit());
                                        self.readable_half = Half::Second;
                                        Half::Second
                                    } else {
                                        last_read_half
                                    }
                                }
                                Half::Second => {
                                    if first_half_is_done {
                                        self.channel.ifcr().write(|w| w.$chtifX().set_bit());
                                        self.readable_half = Half::First;
                                        Half::First
                                    } else {
                                        last_read_half
                                    }
                                }
                            })
                        }
                        pub fn release(mut self) -> (&'static mut [B; 2], $CX, RE) {
                            self.channel.ccr().modify(|_, w| {w.en().clear_bit()});
                            (self.buffer, self.channel, self.resource)
                        }
                    }
                    impl<BUFFER, PAYLOAD, MODE> Transfer<MODE, BUFFER, $CX, PAYLOAD> {
                        pub fn is_done(&self) -> bool {
                            self.channel.tranfer_is_complete()
                        }
                        pub fn wait(mut self) -> (BUFFER, $CX, PAYLOAD) {
                            let buff = self.channel.wait_for_tranfer(self.buffer);
                            (buff, self.channel, self.payload)
                        }
                    }
                    impl<BUFFER, PAYLOAD> Transfer<W, &'static mut BUFFER, $CX, PAYLOAD> {
                        pub fn peek<T>(&self) -> &[T]
                        where
                            BUFFER: AsRef<[T]>,
                        {
                            let pending = self.channel.get_cndtr() as usize;
                            let slice = self.buffer.as_ref();
                            let capacity = slice.len();
                            &slice[..(capacity - pending)]
                        }
                    }
                )+
                impl DmaExt for $DMAX {
                    type Channels = Channels;
                    fn split(self, ahb: &mut AHB) -> Channels {
                        ahb.enr().modify(|_, w| w.$dmaXen().enabled());
                        
                        $(
                            self.$ccrX.reset();
                        )+
                        Channels((), $($CX { _0: () }),+)
                    }
                }
            }
        )+
    }
}
dma! {
    DMA1: (dma1, dma1en, dma1rst, {
        C1: (
            ccr1, CCR1,
            cndtr1, CNDTR1,
            cpar1, CPAR1,
            cmar1, CMAR1,
            htif1, tcif1,
            chtif1, ctcif1, cgif1
        ),
        C2: (
            ccr2, CCR2,
            cndtr2, CNDTR2,
            cpar2, CPAR2,
            cmar2, CMAR2,
            htif2, tcif2,
            chtif2, ctcif2, cgif2
        ),
        C3: (
            ccr3, CCR3,
            cndtr3, CNDTR3,
            cpar3, CPAR3,
            cmar3, CMAR3,
            htif3, tcif3,
            chtif3, ctcif3, cgif3
        ),
        C4: (
            ccr4, CCR4,
            cndtr4, CNDTR4,
            cpar4, CPAR4,
            cmar4, CMAR4,
            htif4, tcif4,
            chtif4, ctcif4, cgif4
        ),
        C5: (
            ccr5, CCR5,
            cndtr5, CNDTR5,
            cpar5, CPAR5,
            cmar5, CMAR5,
            htif5, tcif5,
            chtif5, ctcif5, cgif5
        ),
        C6: (
            ccr6, CCR6,
            cndtr6, CNDTR6,
            cpar6, CPAR6,
            cmar6, CMAR6,
            htif6, tcif6,
            chtif6, ctcif6, cgif6
        ),
        C7: (
            ccr7, CCR7,
            cndtr7, CNDTR7,
            cpar7, CPAR7,
            cmar7, CMAR7,
            htif7, tcif7,
            chtif7, ctcif7, cgif7
        ),
    }),
    DMA2: (dma2, dma2en, dma2rst, {
        C1: (
            ccr1, CCR1,
            cndtr1, CNDTR1,
            cpar1, CPAR1,
            cmar1, CMAR1,
            htif1, tcif1,
            chtif1, ctcif1, cgif1
        ),
        C2: (
            ccr2, CCR2,
            cndtr2, CNDTR2,
            cpar2, CPAR2,
            cmar2, CMAR2,
            htif2, tcif2,
            chtif2, ctcif2, cgif2
        ),
        C3: (
            ccr3, CCR3,
            cndtr3, CNDTR3,
            cpar3, CPAR3,
            cmar3, CMAR3,
            htif3, tcif3,
            chtif3, ctcif3, cgif3
        ),
        C4: (
            ccr4, CCR4,
            cndtr4, CNDTR4,
            cpar4, CPAR4,
            cmar4, CMAR4,
            htif4, tcif4,
            chtif4, ctcif4, cgif4
        ),
        C5: (
            ccr5, CCR5,
            cndtr5, CNDTR5,
            cpar5, CPAR5,
            cmar5, CMAR5,
            htif5, tcif5,
            chtif5, ctcif5, cgif5
        ),
    }),
}
pub trait DmaChannel {
    type Dma;
}