use bytes::Buf;
use bytes::BytesMut;
use serde_v8::JsBuffer;
use serde_v8::V8Slice;
use std::ops::Deref;
use std::ops::DerefMut;
#[derive(Debug)]
pub struct BufView {
  inner: BufViewInner,
  cursor: usize,
}
#[derive(Debug)]
enum BufViewInner {
  Empty,
  Bytes(bytes::Bytes),
  JsBuffer(V8Slice<u8>),
}
impl BufView {
  const fn from_inner(inner: BufViewInner) -> Self {
    Self { inner, cursor: 0 }
  }
  pub const fn empty() -> Self {
    Self::from_inner(BufViewInner::Empty)
  }
  pub fn len(&self) -> usize {
    match &self.inner {
      BufViewInner::Empty => 0,
      BufViewInner::Bytes(bytes) => bytes.len() - self.cursor,
      BufViewInner::JsBuffer(js_buf) => js_buf.len() - self.cursor,
    }
  }
  pub fn is_empty(&self) -> bool {
    self.len() == 0
  }
  pub fn advance_cursor(&mut self, n: usize) {
    assert!(self.len() >= n);
    self.cursor += n;
  }
  pub fn reset_cursor(&mut self) -> usize {
    let old = self.cursor;
    self.cursor = 0;
    old
  }
  pub fn truncate(&mut self, size: usize) {
    match &mut self.inner {
      BufViewInner::Empty => {}
      BufViewInner::Bytes(bytes) => bytes.truncate(size + self.cursor),
      BufViewInner::JsBuffer(buffer) => buffer.truncate(size + self.cursor),
    }
  }
  pub fn split_off(&mut self, at: usize) -> Self {
    let at = at + self.cursor;
    assert!(at <= self.len());
    let other = match &mut self.inner {
      BufViewInner::Empty => BufViewInner::Empty,
      BufViewInner::Bytes(bytes) => BufViewInner::Bytes(bytes.split_off(at)),
      BufViewInner::JsBuffer(buffer) => {
        BufViewInner::JsBuffer(buffer.split_off(at))
      }
    };
    Self {
      inner: other,
      cursor: 0,
    }
  }
  pub fn split_to(&mut self, at: usize) -> Self {
    assert!(at <= self.len());
    let at = at + self.cursor;
    let other = match &mut self.inner {
      BufViewInner::Empty => BufViewInner::Empty,
      BufViewInner::Bytes(bytes) => BufViewInner::Bytes(bytes.split_to(at)),
      BufViewInner::JsBuffer(buffer) => {
        BufViewInner::JsBuffer(buffer.split_to(at))
      }
    };
    let cursor = std::mem::take(&mut self.cursor);
    Self {
      inner: other,
      cursor,
    }
  }
}
impl Buf for BufView {
  fn remaining(&self) -> usize {
    self.len()
  }
  fn chunk(&self) -> &[u8] {
    self.deref()
  }
  fn advance(&mut self, cnt: usize) {
    self.advance_cursor(cnt)
  }
}
impl Deref for BufView {
  type Target = [u8];
  fn deref(&self) -> &[u8] {
    let buf = match &self.inner {
      BufViewInner::Empty => &[],
      BufViewInner::Bytes(bytes) => bytes.deref(),
      BufViewInner::JsBuffer(js_buf) => js_buf.deref(),
    };
    &buf[self.cursor..]
  }
}
impl AsRef<[u8]> for BufView {
  fn as_ref(&self) -> &[u8] {
    self.deref()
  }
}
impl From<JsBuffer> for BufView {
  fn from(buf: JsBuffer) -> Self {
    Self::from_inner(BufViewInner::JsBuffer(buf.into_parts()))
  }
}
impl From<Vec<u8>> for BufView {
  fn from(vec: Vec<u8>) -> Self {
    Self::from_inner(BufViewInner::Bytes(vec.into()))
  }
}
impl From<bytes::Bytes> for BufView {
  fn from(buf: bytes::Bytes) -> Self {
    Self::from_inner(BufViewInner::Bytes(buf))
  }
}
impl From<BufView> for bytes::Bytes {
  fn from(buf: BufView) -> Self {
    match buf.inner {
      BufViewInner::Empty => bytes::Bytes::new(),
      BufViewInner::Bytes(bytes) => bytes,
      BufViewInner::JsBuffer(js_buf) => js_buf.into(),
    }
  }
}
#[derive(Debug)]
pub struct BufMutView {
  inner: BufMutViewInner,
  cursor: usize,
}
#[derive(Debug)]
enum BufMutViewInner {
  JsBuffer(V8Slice<u8>),
  Bytes(BytesMut),
}
impl BufMutView {
  fn from_inner(inner: BufMutViewInner) -> Self {
    Self { inner, cursor: 0 }
  }
  pub fn new(len: usize) -> Self {
    let bytes = BytesMut::zeroed(len);
    Self::from_inner(BufMutViewInner::Bytes(bytes))
  }
  pub fn len(&self) -> usize {
    match &self.inner {
      BufMutViewInner::JsBuffer(js_buf) => js_buf.len() - self.cursor,
      BufMutViewInner::Bytes(bytes) => bytes.len() - self.cursor,
    }
  }
  pub fn is_empty(&self) -> bool {
    self.len() == 0
  }
  pub fn advance_cursor(&mut self, n: usize) {
    assert!(self.len() >= n);
    self.cursor += n;
  }
  pub fn reset_cursor(&mut self) -> usize {
    let old = self.cursor;
    self.cursor = 0;
    old
  }
  pub fn into_view(self) -> BufView {
    let inner = match self.inner {
      BufMutViewInner::JsBuffer(js_buf) => BufViewInner::JsBuffer(js_buf),
      BufMutViewInner::Bytes(bytes) => BufViewInner::Bytes(bytes.into()),
    };
    BufView {
      inner,
      cursor: self.cursor,
    }
  }
  pub fn maybe_unwrap_bytes(self) -> Result<BytesMut, Self> {
    match self.inner {
      BufMutViewInner::JsBuffer(_) => Err(self),
      BufMutViewInner::Bytes(bytes) => Ok(bytes),
    }
  }
  #[must_use = "The result of this method should be tested"]
  #[deprecated = "API will be replaced in the future"]
  #[doc(hidden)]
  pub fn maybe_resize(
    &mut self,
    target_size: usize,
    maximum_increment: usize,
  ) -> Option<usize> {
    if let BufMutViewInner::Bytes(bytes) = &mut self.inner {
      use std::cmp::Ordering::*;
      let len = bytes.len();
      let target_size = target_size + self.cursor;
      match target_size.cmp(&len) {
        Greater => {
          bytes.resize(std::cmp::min(target_size, len + maximum_increment), 0);
        }
        Less => {
          bytes.truncate(target_size);
        }
        Equal => {}
      }
      Some(bytes.len())
    } else {
      None
    }
  }
  #[must_use = "The result of this method should be tested"]
  #[deprecated = "API will be replaced in the future"]
  #[doc(hidden)]
  pub fn maybe_grow(&mut self, target_size: usize) -> Option<usize> {
    if let BufMutViewInner::Bytes(bytes) = &mut self.inner {
      let len = bytes.len();
      let target_size = target_size + self.cursor;
      if target_size > len {
        bytes.resize(target_size, 0);
      }
      Some(bytes.len())
    } else {
      None
    }
  }
  pub fn truncate(&mut self, size: usize) {
    match &mut self.inner {
      BufMutViewInner::Bytes(bytes) => bytes.truncate(size + self.cursor),
      BufMutViewInner::JsBuffer(buffer) => buffer.truncate(size + self.cursor),
    }
    self.cursor = std::cmp::min(self.cursor, self.len());
  }
  pub fn split_off(&mut self, at: usize) -> Self {
    let at = at + self.cursor;
    assert!(at <= self.len());
    let other = match &mut self.inner {
      BufMutViewInner::Bytes(bytes) => {
        BufMutViewInner::Bytes(bytes.split_off(at))
      }
      BufMutViewInner::JsBuffer(buffer) => {
        BufMutViewInner::JsBuffer(buffer.split_off(at))
      }
    };
    Self {
      inner: other,
      cursor: 0,
    }
  }
  pub fn split_to(&mut self, at: usize) -> Self {
    assert!(at <= self.len());
    let at = at + self.cursor;
    let other = match &mut self.inner {
      BufMutViewInner::Bytes(bytes) => {
        BufMutViewInner::Bytes(bytes.split_to(at))
      }
      BufMutViewInner::JsBuffer(buffer) => {
        BufMutViewInner::JsBuffer(buffer.split_to(at))
      }
    };
    let cursor = std::mem::take(&mut self.cursor);
    Self {
      inner: other,
      cursor,
    }
  }
}
impl Buf for BufMutView {
  fn remaining(&self) -> usize {
    self.len()
  }
  fn chunk(&self) -> &[u8] {
    self.deref()
  }
  fn advance(&mut self, cnt: usize) {
    self.advance_cursor(cnt)
  }
}
impl Deref for BufMutView {
  type Target = [u8];
  fn deref(&self) -> &[u8] {
    let buf = match &self.inner {
      BufMutViewInner::JsBuffer(js_buf) => js_buf.deref(),
      BufMutViewInner::Bytes(vec) => vec.deref(),
    };
    &buf[self.cursor..]
  }
}
impl DerefMut for BufMutView {
  fn deref_mut(&mut self) -> &mut [u8] {
    let buf = match &mut self.inner {
      BufMutViewInner::JsBuffer(js_buf) => js_buf.deref_mut(),
      BufMutViewInner::Bytes(vec) => vec.deref_mut(),
    };
    &mut buf[self.cursor..]
  }
}
impl AsRef<[u8]> for BufMutView {
  fn as_ref(&self) -> &[u8] {
    self.deref()
  }
}
impl AsMut<[u8]> for BufMutView {
  fn as_mut(&mut self) -> &mut [u8] {
    self.deref_mut()
  }
}
impl From<JsBuffer> for BufMutView {
  fn from(buf: JsBuffer) -> Self {
    Self::from_inner(BufMutViewInner::JsBuffer(buf.into_parts()))
  }
}
impl From<BytesMut> for BufMutView {
  fn from(buf: BytesMut) -> Self {
    Self::from_inner(BufMutViewInner::Bytes(buf))
  }
}
pub enum WriteOutcome {
  Partial { nwritten: usize, view: BufView },
  Full { nwritten: usize },
}
impl WriteOutcome {
  pub fn nwritten(&self) -> usize {
    match self {
      WriteOutcome::Partial { nwritten, .. } => *nwritten,
      WriteOutcome::Full { nwritten } => *nwritten,
    }
  }
}
#[cfg(test)]
mod tests {
  use super::*;
  #[test]
  pub fn bufview_read_and_truncate() {
    let mut buf = BufView::from(vec![1, 2, 3, 4]);
    assert_eq!(4, buf.len());
    assert_eq!(0, buf.cursor);
    assert_eq!(1, buf.get_u8());
    assert_eq!(3, buf.len());
    buf.truncate(2);
    assert_eq!(2, buf.len());
    assert_eq!(2, buf.get_u8());
    assert_eq!(1, buf.len());
    buf.reset_cursor();
    assert_eq!(3, buf.len());
  }
  #[test]
  pub fn bufview_split() {
    let mut buf = BufView::from(Vec::from_iter(0..100));
    assert_eq!(100, buf.len());
    buf.advance_cursor(25);
    assert_eq!(75, buf.len());
    let mut other = buf.split_off(10);
    assert_eq!(25, buf.cursor);
    assert_eq!(10, buf.len());
    assert_eq!(65, other.len());
    let other2 = other.split_to(20);
    assert_eq!(20, other2.len());
    assert_eq!(45, other.len());
    assert_eq!(100, buf.cursor + buf.len() + other.len() + other2.len());
    buf.reset_cursor();
    assert_eq!(100, buf.cursor + buf.len() + other.len() + other2.len());
  }
  #[test]
  pub fn bufmutview_read_and_truncate() {
    let mut buf = BufMutView::from(BytesMut::from([1, 2, 3, 4].as_slice()));
    assert_eq!(4, buf.len());
    assert_eq!(0, buf.cursor);
    assert_eq!(1, buf.get_u8());
    assert_eq!(3, buf.len());
    buf.truncate(2);
    assert_eq!(2, buf.len());
    assert_eq!(2, buf.get_u8());
    assert_eq!(1, buf.len());
    buf.reset_cursor();
    assert_eq!(3, buf.len());
  }
  #[test]
  pub fn bufmutview_split() {
    let mut buf =
      BufMutView::from(BytesMut::from(Vec::from_iter(0..100).as_slice()));
    assert_eq!(100, buf.len());
    buf.advance_cursor(25);
    assert_eq!(75, buf.len());
    let mut other = buf.split_off(10);
    assert_eq!(25, buf.cursor);
    assert_eq!(10, buf.len());
    assert_eq!(65, other.len());
    let other2 = other.split_to(20);
    assert_eq!(20, other2.len());
    assert_eq!(45, other.len());
    assert_eq!(100, buf.cursor + buf.len() + other.len() + other2.len());
    buf.reset_cursor();
    assert_eq!(100, buf.cursor + buf.len() + other.len() + other2.len());
  }
  #[test]
  #[allow(deprecated)]
  fn bufmutview_resize() {
    let new =
      || BufMutView::from(BytesMut::from(Vec::from_iter(0..100).as_slice()));
    let mut buf = new();
    assert_eq!(100, buf.len());
    buf.maybe_resize(200, 10).unwrap();
    assert_eq!(110, buf.len());
    let mut buf = new();
    assert_eq!(100, buf.len());
    buf.maybe_resize(200, 100).unwrap();
    assert_eq!(200, buf.len());
    let mut buf = new();
    assert_eq!(100, buf.len());
    buf.maybe_resize(200, 1000).unwrap();
    assert_eq!(200, buf.len());
    let mut buf = new();
    buf.advance_cursor(50);
    assert_eq!(50, buf.len());
    buf.maybe_resize(100, 100).unwrap();
    assert_eq!(100, buf.len());
    buf.reset_cursor();
    assert_eq!(150, buf.len());
  }
  #[test]
  #[allow(deprecated)]
  fn bufmutview_grow() {
    let new =
      || BufMutView::from(BytesMut::from(Vec::from_iter(0..100).as_slice()));
    let mut buf = new();
    assert_eq!(100, buf.len());
    buf.maybe_grow(200).unwrap();
    assert_eq!(200, buf.len());
    let mut buf = new();
    buf.advance_cursor(50);
    assert_eq!(50, buf.len());
    buf.maybe_grow(100).unwrap();
    assert_eq!(100, buf.len());
    buf.reset_cursor();
    assert_eq!(150, buf.len());
  }
}