use std::fmt;
use thiserror::Error;

#[derive(Debug, Error)]
pub enum BufferError {
    #[error("ReadPointerAdvanceError({0}, {1}, {2})")]
    RpAdv(usize, usize, usize),
    #[error("WritePointerAdvanceError({0}, {1}, {2})")]
    WpAdv(usize, usize, usize),
}

pub struct Buffer {
    buf: Vec<u8>,
    wp: usize,
    rp: usize,
}

impl Buffer {
    pub fn new(cap: usize) -> Buffer {
        Self {
            buf: vec![0; cap],
            wp: 0,
            rp: 0,
        }
    }

    #[inline(always)]
    pub fn reset(&mut self) {
        self.rp = 0;
        self.wp = 0;
    }

    #[inline(always)]
    pub fn len(&self) -> usize {
        self.wp - self.rp
    }

    #[inline(always)]
    pub fn free_len(&self) -> usize {
        self.buf.len() - self.wp
    }

    pub fn writable(&mut self) -> &mut [u8] {
        self.check_invariant();
        self.wrap_if_needed();
        &mut self.buf[self.wp..]
    }

    pub fn readable(&self) -> &[u8] {
        self.check_invariant();
        &self.buf[self.rp..self.wp]
    }

    #[inline(always)]
    pub fn advance(&mut self, n: usize) -> Result<(), BufferError> {
        self.check_invariant();
        if self.rp + n > self.wp {
            Err(BufferError::RpAdv(self.wp, self.rp, n))
        } else {
            self.rp += n;
            Ok(())
        }
    }

    pub fn adv_wp(&mut self, n: usize) -> Result<(), BufferError> {
        self.check_invariant();
        if self.wp + n > self.buf.len() {
            Err(BufferError::WpAdv(self.wp, self.rp, n))
        } else {
            self.wp += n;
            Ok(())
        }
    }

    fn wrap_if_needed(&mut self) {
        self.check_invariant();
        if self.wp == 0 {
        } else if self.rp == self.wp {
            self.rp = 0;
            self.wp = 0;
        } else if self.rp > self.buf.len() >> 1 {
            let len = self.len();
            unsafe {
                let src = self.buf[self.rp..].as_ptr();
                let dst = self.buf[..].as_mut_ptr();
                std::ptr::copy(src, dst, len);
            }
            self.rp = 0;
            self.wp = len;
        }
    }

    #[inline(always)]
    fn check_invariant(&self) {
        if false {
            self.check_invariant_inner()
        }
    }

    fn check_invariant_inner(&self) {
        if self.wp > self.buf.len() {
            eprintln!("ERROR  wp {}  rp {}", self.wp, self.rp);
        }
        if self.rp > self.wp {
            eprintln!("ERROR  wp {}  rp {}", self.wp, self.rp);
        }
        assert!(self.wp <= self.buf.len());
        assert!(self.rp <= self.wp);
    }
}

impl fmt::Display for Buffer {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Buffer {{ wp: {}, rp: {} }}", self.wp, self.rp)
    }
}

#[test]
fn test_vec_index() {
    const BUFFER_CAP: usize = 256;
    let mut buf = vec![0u8; BUFFER_CAP];
    let a = &mut buf[BUFFER_CAP - 1..BUFFER_CAP];
    a[0] = 123;
    let a = &mut buf[BUFFER_CAP..];
    assert_eq!(a.len(), 0);
}
