#![no_std]
extern crate alloc;
use alloc::boxed::Box;
use alloc::collections::VecDeque;
use alloc::vec::Vec;
use core::cmp::min;
use core::ops::{Index, IndexMut};
pub const DEFAULT_CHUNK_SIZE: usize = 256;
pub type ChunkedBuffer = GenericChunkedBuffer<DEFAULT_CHUNK_SIZE>;
pub struct GenericChunkedBuffer<const CHUNK_SIZE: usize> {
    write_pos: usize,
    read_pos: usize,
    chunks: VecDeque<Box<[u8; CHUNK_SIZE]>>,
}
impl<const CHUNK_SIZE: usize> GenericChunkedBuffer<CHUNK_SIZE> {
    pub fn new() -> Self {
        let mut s = GenericChunkedBuffer {
            write_pos: 0,
            read_pos: 0,
            chunks: VecDeque::new(),
        };
        s.chunks.push_back(Box::new([0; CHUNK_SIZE]));
        s
    }
    pub fn len(&self) -> usize {
        (self.chunks.len() - 1) * CHUNK_SIZE + self.write_pos - self.read_pos
    }
    pub fn is_empty(&self) -> bool {
        self.chunks.len() == 1 && self.write_pos == self.read_pos
    }
    pub fn read(&mut self, dest: &mut [u8]) -> usize {
        let mut nread = 0;
        while nread < dest.len() && !self.is_empty() {
            let chunk = &*self.chunks[0];
            let start = self.read_pos;
            let mut end = min(CHUNK_SIZE, start + dest.len() - nread);
            if self.chunks.len() == 1 {
                end = end.min(self.write_pos);
            }
            let n = end - start;
            dest[nread..nread + n].copy_from_slice(&chunk[start..end]);
            if end == CHUNK_SIZE {
                self.read_pos = 0;
                self.chunks.pop_front();
                } else {
                self.read_pos = end;
            }
            nread += n;
        }
        nread
    }
    pub fn write(&mut self, src: &[u8]) {
        let mut nwritten = 0;
        while nwritten < src.len() {
            let chunk = &mut **self.chunks.back_mut().unwrap();
            let start = self.write_pos;
            let end = min(CHUNK_SIZE, start + src.len() - nwritten);
            let n = end - start;
            chunk[start..end].copy_from_slice(&src[nwritten..nwritten + n]);
            nwritten += n;
            if end == CHUNK_SIZE {
                self.write_pos = 0;
                self.chunks.push_back(Box::new([0; CHUNK_SIZE]));
            } else {
                self.write_pos = end;
            }
        }
    }
    pub fn iter(&self) -> Iter<CHUNK_SIZE> {
        Iter::new(self)
    }
    pub fn iter_chunks(&self) -> IterChunk<CHUNK_SIZE> {
        IterChunk::new(self)
    }
    pub fn as_vec(&self) -> Vec<u8> {
        let mut vec = Vec::with_capacity(self.len());
        for chunk in self.iter_chunks() {
            vec.extend_from_slice(chunk);
        }
        vec
    }
}
impl<const CHUNK_SIZE: usize> Default for GenericChunkedBuffer<CHUNK_SIZE> {
    fn default() -> Self {
        Self::new()
    }
}
impl<const CHUNK_SIZE: usize> Index<usize> for GenericChunkedBuffer<CHUNK_SIZE> {
    type Output = u8;
    fn index(&self, index: usize) -> &Self::Output {
        if self.is_empty() {
            panic!("indexed into an empty buffer");
        }
        if index >= self.len() {
            panic!("out of bounds access");
        }
        let pos = index % CHUNK_SIZE;
        let index = index / CHUNK_SIZE;
        &self.chunks[index][pos]
    }
}
impl<const CHUNK_SIZE: usize> IndexMut<usize> for GenericChunkedBuffer<CHUNK_SIZE> {
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        if self.is_empty() {
            panic!("indexed into an empty buffer");
        }
        if index >= self.len() {
            panic!("out of bounds access");
        }
        let pos = index % CHUNK_SIZE;
        let index = index / CHUNK_SIZE;
        &mut self.chunks[index][pos]
    }
}
pub struct Iter<'a, const CHUNK_SIZE: usize> {
    nread: usize,
    index: usize,
    read_pos: usize,
    buf: &'a GenericChunkedBuffer<CHUNK_SIZE>,
}
impl<'a, const CHUNK_SIZE: usize> Iter<'a, CHUNK_SIZE> {
    fn new(buf: &'a GenericChunkedBuffer<CHUNK_SIZE>) -> Self {
        Iter {
            nread: 0,
            index: 0,
            read_pos: buf.read_pos,
            buf,
        }
    }
}
impl<const CHUNK_SIZE: usize> Iterator for Iter<'_, CHUNK_SIZE> {
    type Item = u8;
    fn next(&mut self) -> Option<Self::Item> {
        if self.nread == self.buf.len() {
            None
        } else {
            let byte = self.buf.chunks[self.index][self.read_pos];
            self.nread += 1;
            self.read_pos += 1;
            if self.read_pos == CHUNK_SIZE {
                self.read_pos = 0;
                self.index += 1;
            }
            Some(byte)
        }
    }
}
pub struct IterChunk<'a, const CHUNK_SIZE: usize> {
    index: usize,
    buf: &'a GenericChunkedBuffer<CHUNK_SIZE>,
}
impl<'a, const CHUNK_SIZE: usize> IterChunk<'a, CHUNK_SIZE> {
    fn new(buf: &'a GenericChunkedBuffer<CHUNK_SIZE>) -> Self {
        IterChunk { index: 0, buf }
    }
}
impl<'a, const CHUNK_SIZE: usize> Iterator for IterChunk<'a, CHUNK_SIZE> {
    type Item = &'a [u8];
    fn next(&mut self) -> Option<Self::Item> {
        if self.index == self.buf.chunks.len() {
            None
        } else {
            let begin = if self.index == 0 {
                self.buf.read_pos
            } else {
                0
            };
            let end = if self.index + 1 == self.buf.chunks.len() {
                self.buf.write_pos
            } else {
                CHUNK_SIZE
            };
            let slice = &self.buf.chunks[self.index][begin..end];
            self.index += 1;
            if slice.is_empty() {
                None
            } else {
                Some(slice)
            }
        }
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    type Buf = GenericChunkedBuffer<4>;
    #[test]
    fn test() {
        let mut buf = Buf::new();
        assert_eq!(buf.len(), 0);
        assert!(buf.is_empty());
        let mut dest = [0; 10];
        assert_eq!(buf.read(&mut dest), 0);
        buf.write(&[1, 2, 3]);
        assert_eq!(buf.len(), 3);
        assert!(!buf.is_empty());
        let mut dest = [0; 10];
        assert_eq!(buf.read(&mut dest), 3);
        assert_eq!(dest, [1, 2, 3, 0, 0, 0, 0, 0, 0, 0]);
        assert_eq!(buf.len(), 0);
        assert!(buf.is_empty());
        buf.write(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
        assert_eq!(buf.len(), 10);
        assert!(!buf.is_empty());
        let mut dest = [0; 9];
        assert_eq!(buf.read(&mut dest), 9);
        assert_eq!(dest, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
        assert_eq!(buf.len(), 1);
        let mut dest = [0; 9];
        assert_eq!(buf.read(&mut dest), 1);
        assert_eq!(dest, [10, 0, 0, 0, 0, 0, 0, 0, 0]);
        assert_eq!(buf.len(), 0);
        assert!(buf.is_empty());
        buf.write(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
        assert_eq!(buf.len(), 10);
        assert!(!buf.is_empty());
        let mut dest = [0; 5];
        assert_eq!(buf.read(&mut dest), 5);
        assert_eq!(dest, [1, 2, 3, 4, 5]);
        assert_eq!(buf.len(), 5);
        assert!(!buf.is_empty());
        let mut dest = [0; 5];
        assert_eq!(buf.read(&mut dest), 5);
        assert_eq!(dest, [6, 7, 8, 9, 10]);
        assert_eq!(buf.len(), 0);
        assert!(buf.is_empty());
        let mut dest = [0; 5];
        buf.write(&[99]);
        assert_eq!(buf.len(), 1);
        assert_eq!(buf.read(&mut dest), 1);
        assert_eq!(dest, [99, 0, 0, 0, 0]);
    }
    #[test]
    fn test_iterator() {
        let mut buf = Buf::new();
        let mut i = buf.iter();
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        buf = Buf::new();
        buf.write(&[]);
        i = buf.iter();
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        buf = Buf::new();
        buf.write(&[1, 2, 3]);
        i = buf.iter();
        for count in 1..4 {
            assert_eq!(i.next(), Some(count));
        }
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        buf = Buf::new();
        buf.write(&[1, 2, 3, 4]);
        i = buf.iter();
        for count in 1..5 {
            assert_eq!(i.next(), Some(count));
        }
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        buf = Buf::new();
        buf.write(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
        i = buf.iter();
        for count in 1..11 {
            assert_eq!(i.next(), Some(count));
        }
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        buf = Buf::new();
        buf.write(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
        i = buf.iter();
        for count in 1..13 {
            assert_eq!(i.next(), Some(count));
        }
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        buf = Buf::new();
        buf.write(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]);
        i = buf.iter();
        for count in 1..14 {
            assert_eq!(i.next(), Some(count));
        }
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
    }
    #[test]
    fn test_iter_chunks() {
        let mut buf = Buf::new();
        let mut i = buf.iter_chunks();
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        buf = Buf::new();
        buf.write(&[]);
        i = buf.iter_chunks();
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        buf = Buf::new();
        buf.write(&[1, 2, 3]);
        i = buf.iter_chunks();
        assert_eq!(i.next(), Some(&[1, 2, 3][..]));
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        buf = Buf::new();
        buf.write(&[1, 2, 3, 4]);
        i = buf.iter_chunks();
        assert_eq!(i.next(), Some(&[1, 2, 3, 4][..]));
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        buf = Buf::new();
        buf.write(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
        i = buf.iter_chunks();
        assert_eq!(i.next(), Some(&[1, 2, 3, 4][..]));
        assert_eq!(i.next(), Some(&[5, 6, 7, 8][..]));
        assert_eq!(i.next(), Some(&[9, 10][..]));
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        buf = Buf::new();
        buf.write(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
        i = buf.iter_chunks();
        assert_eq!(i.next(), Some(&[1, 2, 3, 4][..]));
        assert_eq!(i.next(), Some(&[5, 6, 7, 8][..]));
        assert_eq!(i.next(), Some(&[9, 10, 11, 12][..]));
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        buf = Buf::new();
        buf.write(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]);
        i = buf.iter_chunks();
        assert_eq!(i.next(), Some(&[1, 2, 3, 4][..]));
        assert_eq!(i.next(), Some(&[5, 6, 7, 8][..]));
        assert_eq!(i.next(), Some(&[9, 10, 11, 12][..]));
        assert_eq!(i.next(), Some(&[13][..]));
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
        assert_eq!(i.next(), None);
    }
    #[test]
    fn test_index() {
        let mut buf = Buf::new();
        buf.write(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]);
        for i in 0..14 {
            assert_eq!(buf[i], i as u8);
        }
    }
    #[test]
    #[should_panic]
    fn test_index_panic_on_empty() {
        let buf = Buf::new();
        buf[0];
    }
    #[test]
    #[should_panic]
    fn test_index_out_of_bounds() {
        let mut buf = Buf::new();
        buf.write(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]);
        buf[13];
    }
    #[test]
    fn test_index_mut() {
        let mut buf = Buf::new();
        buf.write(&[0u8; 14]);
        for i in 0..14 {
            buf[i] = i as u8;
        }
        let mut want = Buf::new();
        want.write(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]);
        assert_eq!(buf.as_vec(), want.as_vec());
    }
    #[test]
    #[should_panic]
    fn test_index_mut_panic_on_empty() {
        let mut buf = Buf::new();
        buf[0] = 3;
    }
    #[test]
    #[should_panic]
    fn test_index_mut_out_of_bounds() {
        let mut buf = Buf::new();
        buf.write(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]);
        buf[13] = 3;
    }
}