use tracing::trace;

/// Takes a slice as input and divides it into smaller slices.
/// This processes the slice from start to end by user-provided _len_ sized chunks.
///
/// For example `&[1, 2, 3, 4] --> &[1, 2] and &[3, 4]`.
///
/// Uses:
/// - When you know the length to slice at runtime but not compile time.
/// - When parsing formats within formats. For example,
///   the DHCP format has an options field that contains TLV
///   formatted options. See `options/options.rs`.
#[derive(Debug)]
pub struct Slicer<'de> {
    input: &'de [u8],
    input_index: usize,
}

impl<'de> Slicer<'de> {
    /// Creates an instance of Slicer.
    pub fn new(input: &'de [u8]) -> Self {
        Slicer {
            input,
            input_index: 0,
        }
    }

    /// Returns the next len number of bytes and changes the current position the same amount.
    ///
    /// Returns an Option instead of panicking. Use `slice_unchecked` if it is okay to panic.
    #[inline]
    pub fn slice(&mut self, len: usize) -> Option<&'de [u8]> {
        trace!("slice");
        let slice = self.input.get(self.input_index..self.input_index + len);

        if slice.is_some() {
            self.input_index += len;
        }

        slice
    }

    /// Returns the next len number of bytes and changes the current position the same amount.
    ///
    /// Can panic if slicing past array length. Use `slice` for no panic.
    #[inline]
    pub fn slice_unchecked(&mut self, len: usize) -> &'de [u8] {
        trace!("slice_unchecked");
        let slice = &self.input[self.input_index..self.input_index + len];
        self.input_index += len;
        slice
    }

    /// Returns the remaining bytes in the input and resets the current position to the start.
    #[inline]
    pub fn slice_remainder(&mut self) -> &'de [u8] {
        trace!("slice_remainder");
        let slice = &self.input[self.input_index..];
        self.reset();
        slice
    }

    /// Slicer keeps track of how many bytes have been sliced (the position from start).
    /// This resets that internal state.
    #[inline]
    pub fn reset(&mut self) {
        self.input_index = 0;
    }
}
