use crate::decode::{__read_marker, read_arg};
use crate::{Error, Marker, ReadBuffer, ReadBufferBorrowed};

/// Decode an unsigned value given any suitable representation.
#[inline(always)]
pub fn read_unsigned(buf: &mut ReadBuffer) -> Result<usize, Error> {
    __read_unsigned(&buf.borrow())
}

/// Decode an 8-bit signed value.
#[inline(always)]
pub fn read_u8(buf: &mut ReadBuffer) -> Result<u8, Error> {
    __read_u8(&buf.borrow())
}

/// Decode a 16-bit unsigned value.
#[inline(always)]
pub fn read_u16(buf: &mut ReadBuffer) -> Result<u16, Error> {
    __read_u16(&buf.borrow())
}

/// Decode a 32-bit unsigned value.
#[inline(always)]
pub fn read_u32(buf: &mut ReadBuffer) -> Result<u32, Error> {
    __read_u32(&buf.borrow())
}

/// Decode a 64-bit unsigned value.
#[inline(always)]
pub fn read_u64(buf: &mut ReadBuffer) -> Result<u64, Error> {
    __read_u64(&buf.borrow())
}

pub(crate) fn __read_unsigned(buf: &ReadBufferBorrowed) -> Result<usize, Error> {
    let snapshot = buf.snapshot();
    match __read_marker(buf)? {
        Marker::ImmPos { value } => Ok(value as _),

        Marker::U8 => read_arg!(buf as u8).map(|v| v as _),
        Marker::U16 => read_arg!(buf as u16).map(|v| v as _),
        Marker::U32 => read_arg!(buf as u32).map(|v| v as _),
        Marker::U64 => read_arg!(buf as u64).map(|v| v as _),

        _ => {
            // Undo the read_marker call.
            buf.restore(snapshot);

            Err(Error::Unexpected)
        }
    }
}

pub(crate) fn __read_u8(buf: &ReadBufferBorrowed) -> Result<u8, Error> {
    let marker = buf.peek(1, |buf| {
        // Safety: 1 byte available in buf.
        unsafe { *buf.get_unchecked(0) }
    })?;

    if marker != Marker::U8.encoding() {
        return Err(Error::Unexpected);
    }

    // Discard the marker.
    // Safety: there is at least one byte in the buffer because peek returned.
    let _ = buf.discard(1);

    read_arg!(buf as u8)
}

pub(crate) fn __read_u16(buf: &ReadBufferBorrowed) -> Result<u16, Error> {
    let marker = buf.peek(1, |buf| {
        // Safety: 1 byte available in buf.
        unsafe { *buf.get_unchecked(0) }
    })?;

    if marker != Marker::U16.encoding() {
        return Err(Error::Unexpected);
    }

    // Discard the marker.
    // Safety: there is at least one byte in the buffer because peek returned.
    let _ = buf.discard(1);

    read_arg!(buf as u16)
}

pub(crate) fn __read_u32(buf: &ReadBufferBorrowed) -> Result<u32, Error> {
    let marker = buf.peek(1, |buf| {
        // Safety: 1 byte available in buf.
        unsafe { *buf.get_unchecked(0) }
    })?;

    if marker != Marker::U32.encoding() {
        return Err(Error::Unexpected);
    }

    // Discard the marker.
    // Safety: there is at least one byte in the buffer because peek returned.
    let _ = buf.discard(1);

    read_arg!(buf as u32)
}

pub(crate) fn __read_u64(buf: &ReadBufferBorrowed) -> Result<u64, Error> {
    let marker = buf.peek(1, |buf| {
        // Safety: 1 byte available in buf.
        unsafe { *buf.get_unchecked(0) }
    })?;

    if marker != Marker::U64.encoding() {
        return Err(Error::Unexpected);
    }

    // Discard the marker.
    // Safety: there is at least one byte in the buffer because peek returned.
    let _ = buf.discard(1);

    read_arg!(buf as u64)
}
