//! Audio buffer type to transfer audio data within different software/hardware components.
#![warn(missing_docs)]

use core::cell::{Cell, UnsafeCell};

use cortex_m::interrupt::{CriticalSection, Mutex};

/// Each packet contains 1 millisecond of audio data (48 samples at 48000kHz).
///
/// This translates two twice the number of values, as each sample contains two channels.
pub const SAMPLES_PER_PACKET: usize = 48;
/// Number of channels for all audio data.
pub const CHANNELS: usize = 2;
/// Number of packets that can be stored in each `AudioBuffer`.
pub const PACKETS: usize = 3;

/// Buffer for a single packet with audio data.
pub type Packet = [u32; SAMPLES_PER_PACKET * CHANNELS];

/// Type for audio packet buffering.
///
/// The audio packets always contain two channels with 32-bit samples. The type contains three
/// buffers each containing the same fixed number of samples. This structure allows two buffers to
/// be in use by the DMA engine while the third is used by the ARM core.
pub struct AudioBuffer {
    mutex: Mutex<AudioBufferInner>,
}

struct AudioBufferInner {
    /// Packet contents.
    packets: [UnsafeCell<Packet>; PACKETS],
    /// Bitmask of packets which contain valid data and are not currently being read.
    valid: Cell<u32>,
    /// Bitmask of packets which do not contain valid data and which are not currently being
    /// written.
    invalid: Cell<u32>,
    /// Bitmask of all packets which are currently borrowed via `borrow_read()`.
    reading: Cell<u32>,
    /// Bitmask of all packets which are currently borrowed via `borrow_write()`.
    writing: Cell<u32>,
}

impl AudioBuffer {
    /// Creates a new audio buffer.
    pub const fn new() -> AudioBuffer {
        // Initially, all packet contents are invalid and no packets are currently being read or
        // written.
        AudioBuffer {
            mutex: Mutex::new(AudioBufferInner {
                packets: [
                    UnsafeCell::new([0; SAMPLES_PER_PACKET * CHANNELS]),
                    UnsafeCell::new([0; SAMPLES_PER_PACKET * CHANNELS]),
                    UnsafeCell::new([0; SAMPLES_PER_PACKET * CHANNELS]),
                ],
                valid: Cell::new(0),
                invalid: Cell::new(((1 << PACKETS) - 1) as u32),
                reading: Cell::new(0),
                writing: Cell::new(0),
            }),
        }
    }

    /// Returns whether there are any buffers with valid data ready for reading.
    pub fn can_read(&self, cs: &CriticalSection) -> bool {
        let inner = self.mutex.borrow(cs);
        inner.valid.get() != 0
    }

    /// Borrows a single buffer for reading, returns `None` if no buffer with valid data is
    /// available.
    ///
    /// The caller needs to return the buffer via `read_finished()` once the data has been
    /// processed.
    pub fn borrow_read(&'static self, cs: &CriticalSection) -> Option<ReadPacket> {
        let inner = self.mutex.borrow(cs);
        let valid = inner.valid.get();
        if valid == 0 {
            return None;
        }
        let index = valid.trailing_zeros();
        inner.valid.set(valid & !(1 << index));
        inner.reading.set(inner.reading.get() | (1 << index));

        let index = index as usize;
        Some(ReadPacket {
            buffer: self,
            data: unsafe { &*inner.packets[index].get() },
            index,
        })
    }

    /// Returns a buffer that was allocated via `borrow_read()` and marks it as empty.
    pub fn read_finished(&'static self, packet: ReadPacket, cs: &CriticalSection) {
        assert!((self as *const _) == (packet.buffer as *const _));
        let inner = self.mutex.borrow(cs);
        let index = packet.index;
        inner.reading.set(inner.reading.get() & !(1 << index));
        inner.invalid.set(inner.invalid.get() | (1 << index));
    }

    /// Returns whether there are any empty buffers ready for writing.
    pub fn can_write(&self, cs: &CriticalSection) -> bool {
        let inner = self.mutex.borrow(cs);
        inner.invalid.get() != 0
    }

    /// Borrows a single buffer for writing, returns `None` if no empty buffer is available.
    ///
    /// The caller needs to return the buffer via `write_finished()` once it was filled with data.
    pub fn borrow_write(&'static self, cs: &CriticalSection) -> Option<WritePacket> {
        let inner = self.mutex.borrow(cs);
        let invalid = inner.invalid.get();
        if invalid == 0 {
            return None;
        }
        let index = invalid.trailing_zeros();
        inner.invalid.set(invalid & !(1 << index));
        inner.writing.set(inner.writing.get() | (1 << index));

        let index = index as usize;
        Some(WritePacket {
            buffer: self,
            data: unsafe { &mut *inner.packets[index].get() },
            index,
        })
    }

    /// Returns a buffer that was allocated via `borrow_write()` and marks it as ready for reading.
    pub fn write_finished(&'static self, packet: WritePacket, cs: &CriticalSection) {
        assert!((self as *const _) == (packet.buffer as *const _));
        let inner = self.mutex.borrow(cs);
        let index = packet.index;
        inner.writing.set(inner.writing.get() & !(1 << index));
        inner.valid.set(inner.valid.get() | (1 << index));
    }
}

/// Packet buffer that has been borrowed for reading (i.e., the buffer contains valid data).
pub struct ReadPacket {
    buffer: &'static AudioBuffer,
    data: &'static Packet,
    index: usize,
}

impl AsRef<[u32]> for ReadPacket {
    fn as_ref(&self) -> &[u32] {
        self.data
    }
}

/// Packet buffer that has been borrowed for writing (i.e., the buffer was previously empty).
pub struct WritePacket {
    buffer: &'static AudioBuffer,
    data: &'static mut Packet,
    index: usize,
}

impl AsRef<[u32]> for WritePacket {
    fn as_ref(&self) -> &[u32] {
        self.data
    }
}

impl AsMut<[u32]> for WritePacket {
    fn as_mut(&mut self) -> &mut [u32] {
        self.data
    }
}
