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

/// Decode a signed value given any suitable representation.
#[inline(always)]
pub fn read_signed(buf: &mut ReadBuffer) -> Result<isize, Error> {
    __read_signed(&buf.borrow())
}

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

/// Decode a 16-bit signed value.
#[inline(always)]
pub fn read_i16(buf: &mut ReadBuffer) -> Result<i16, Error> {
    __read_i16(&buf.borrow())
}

/// Decode a 32-bit signed value.
#[inline(always)]
pub fn read_i32(buf: &mut ReadBuffer) -> Result<i32, Error> {
    __read_i32(&buf.borrow())
}

/// Decode a 64-bit signed value.
#[inline(always)]
pub fn read_i64(buf: &mut ReadBuffer) -> Result<i64, Error> {
    __read_i64(&buf.borrow())
}

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

        Marker::I8 => read_arg!(buf as i8).map(|v| v as _),
        Marker::I16 => read_arg!(buf as i16).map(|v| v as _),
        Marker::I32 => read_arg!(buf as i32).map(|v| v as _),
        Marker::I64 => read_arg!(buf as i64).map(|v| v as _),

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

            Err(Error::Unexpected)
        }
    }
}

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

    if marker != Marker::I8.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 i8)
}

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

    if marker != Marker::I16.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 i16)
}

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

    if marker != Marker::I32.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 i32)
}

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

    if marker != Marker::I64.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 i64)
}
