//! `BitStreamReader` API
//!
//! This module provides an interface to read and write bits (and bytes) for
//! huffman

pub struct BitStreamReader<'src>
{
    // buffer from which we are pulling in bits from
    // used in decompression.
    pub src:       &'src [u8],
    // position in our buffer,
    pub position:  usize,
    pub bits_left: u8,
    pub buffer:    u64,
    pub over_read: usize
}

impl<'src> BitStreamReader<'src>
{
    /// Create a new `BitStreamReader` instance
    ///
    /// # Expectations
    /// The buffer must be padded with fill bytes in the end,
    /// if not, this becomes UB in the refill phase.
    pub fn new(in_buffer: &'src [u8]) -> BitStreamReader<'src>
    {
        BitStreamReader {
            bits_left: 0,
            buffer:    0,
            src:       in_buffer,
            position:  0,
            over_read: 0
        }
    }
    /// Refill the bitstream ensuring the buffer has bits between
    /// 56 and 63.
    ///
    #[inline(always)]
    pub fn refill(&mut self)
    {
        /*
         * The refill always guarantees refills between 56-63
         *
         * Bits stored will never go above 63 and if bits are in the range 56-63 no refills occur.
         */
        let mut buf = [0; 8];

        match self.src.get(self.position..self.position + 8)
        {
            Some(bytes) =>
            {
                buf.copy_from_slice(bytes);
                // create a u64 from an array of u8's
                let new_buffer = u64::from_le_bytes(buf);
                // num indicates how many bytes we actually consumed.
                let num = 63 ^ self.bits_left;
                // offset position
                self.position += (num >> 3) as usize;
                // shift number of bits
                self.buffer |= new_buffer << self.bits_left;
                // update bits left
                // bits left are now between 56-63
                self.bits_left |= 56;
            }
            None => self.refill_slow()
        }
    }
    #[inline(always)]
    pub fn refill_inner_loop(&mut self)
    {
        /*
         * The refill always guarantees refills between 56-63
         *
         * Bits stored will never go above 63 and if bits are in the range 56-63 no refills occur.
         */
        let mut buf = [0; 8];

        if let Some(bytes) = self.src.get(self.position..self.position + 8)
        {
            {
                buf.copy_from_slice(bytes);
                // create a u64 from an array of u8's
                let new_buffer = u64::from_le_bytes(buf);
                // num indicates how many bytes we actually consumed.
                let num = 63 ^ self.bits_left;
                // offset position
                self.position += (num >> 3) as usize;
                // shift number of bits
                self.buffer |= new_buffer << self.bits_left;
                // update bits left
                // bits left are now between 56-63
                self.bits_left |= 56;
            }
        }
    }
    #[inline(never)]
    fn refill_slow(&mut self)
    {
        let bytes = &self.src[self.position..];

        for byte in bytes
        {
            if self.bits_left >= 56
            {
                break;
            }

            self.buffer |= u64::from(*byte) << self.bits_left;
            self.bits_left += 8;
            self.position += 1;
        }
        while self.bits_left < 56
        {
            self.bits_left += 8;
            self.over_read += 1;
        }
    }

    #[inline(always)]
    pub const fn peek_bits<const LOOKAHEAD: usize>(&self) -> usize
    {
        (self.buffer & ((1 << LOOKAHEAD) - 1)) as usize
    }
    #[inline(always)]
    pub fn peek_var_bits(&self, lookahead: usize) -> usize
    {
        debug_assert!(self.bits_left >= lookahead as u8);
        (self.buffer & ((1 << lookahead) - 1)) as usize
    }

    #[inline(always)]
    pub fn get_bits(&mut self, num_bits: u8) -> u64
    {
        debug_assert!(self.bits_left >= num_bits);

        let mask = (1_u64 << num_bits) - 1;

        let value = self.buffer & mask;

        self.buffer >>= num_bits;

        self.bits_left -= num_bits;

        value
    }
    /// Get number of bits left in the bit buffer.
    pub const fn get_bits_left(&self) -> u8
    {
        self.bits_left
    }
    /// Get position the stream is in this buffer
    /// Or alternatively, number of bits read.
    pub fn get_position(&self) -> usize
    {
        self.position
            .saturating_sub(usize::from(self.bits_left >> 3))
    }

    /// Reset buffer and bits left to zero.
    pub fn reset(&mut self)
    {
        self.buffer = 0;
        self.bits_left = 0;
    }
    /// Return true if the bit buffer can satisfy
    /// `bits` read without refilling,
    pub const fn has(&self, bits: u8) -> bool
    {
        self.bits_left >= bits
    }

    #[inline(always)]
    pub fn drop_bits(&mut self, bits: u8)
    {
        debug_assert!(self.bits_left >= bits);
        self.bits_left -= bits;
        self.buffer >>= bits;
    }
    /// Return the remaining bytes in this stream.
    ///
    /// This does not consider bits in the bit-buffer hence
    /// may not be accurate
    pub const fn remaining_bytes(&self) -> usize
    {
        self.src.len().saturating_sub(self.position)
    }
}
