use crate::{Storage, StorageMut};

/// The main structure
///
/// Keeps track of the data storage and a cursor for automatic operations
#[derive(Debug)]
pub struct BitBuf<S> {
  data: S,
  cursor: usize,
}

impl<S> BitBuf<S> {
  /// Construct from provided data storage
  pub const fn from(data: S) -> Self {
    Self { data, cursor: 0 }
  }

  /// Get the underlying bytes from the storage
  #[inline(always)]
  pub fn bytes(&self) -> &[u8]
  where
    S: Storage,
  {
    self.data.as_bytes()
  }

  /// Get the underlying bytes from the storage
  #[inline(always)]
  pub fn bytes_mut(&mut self) -> &mut [u8]
  where
    S: StorageMut,
  {
    self.data.as_bytes_mut()
  }

  /// Get the internal cursor for automatic operations
  ///
  /// <div class="warning">
  ///
  /// The cursor may not be within storage bounds
  ///
  /// </div>
  #[inline(always)]
  pub fn pos(&self) -> usize {
    self.cursor
  }

  /// Get the byte-aligned position of the internal cursor for automatic operations
  ///
  /// <div class="warning">
  ///
  /// The cursor may not be within storage bounds
  ///
  /// </div>
  #[inline(always)]
  pub fn byte_pos(&self) -> usize {
    self.cursor / 8
  }

  /// Advance the internal cursor for automatic operations
  ///
  /// <div class="warning">
  ///
  /// This does not enforce that the cursor stay within storage bounds
  ///
  /// </div>
  #[inline(always)]
  pub fn advance(&mut self, bits: usize) -> &mut Self {
    self.cursor += bits;
    self
  }

  /// Advance the internal cursor for automatic operations by a number of bytes
  ///
  /// <div class="warning">
  ///
  /// This does not enforce that the cursor stay within storage bounds
  ///
  /// </div>
  #[inline(always)]
  pub fn advance_bytes(&mut self, bytes: usize) -> &mut Self {
    self.advance(bytes * 8)
  }

  /// Set the internal cursor for automatic operations
  ///
  /// <div class="warning">
  ///
  /// This does not enforce that the cursor stay within storage bounds
  ///
  /// </div>
  #[inline(always)]
  pub fn seek(&mut self, offset: usize) -> &mut Self {
    self.cursor = offset;
    self
  }

  /// Set the internal cursor for automatic operations to a byte position
  ///
  /// This sets the cursor to `offset * 8` bits.
  ///
  /// <div class="warning">
  ///
  /// This does not enforce that the cursor stay within storage bounds
  ///
  /// </div>
  #[inline(always)]
  pub fn seek_byte(&mut self, byte: usize) -> &mut Self {
    self.seek(byte * 8)
  }

  /// Checks if the internal cursor for automatic operations is aligned on the start of a byte
  ///
  /// <div class="warning">
  ///
  /// This does not care whether the cursor is outside storage bounds
  ///
  /// </div>
  #[inline(always)]
  pub fn is_aligned(&self) -> bool {
    self.cursor.is_multiple_of(8)
  }
}
