use crate::{Error, Marker, WriteBuffer};

/// Encode the most efficient representation of an unsigned value.
pub fn write_unsigned(buf: &mut WriteBuffer, value: usize) -> Result<Marker, Error> {
    if value < 128 {
        let marker = Marker::ImmPos { value: value as _ };
        buf.write(1, |buf| {
            // Safety: 1 byte available in buf.
            unsafe { *buf.as_mut_ptr() = marker.encoding() }
        })?;

        Ok(marker)
    } else if value <= u8::MAX as usize {
        write_u8(buf, value as _)
    } else if value <= u16::MAX as usize {
        write_u16(buf, value as _)
    } else if value <= u32::MAX as usize {
        write_u32(buf, value as _)
    } else {
        write_u64(buf, value as _)
    }
}

/// Encode an 8-bit unsigned value.
pub fn write_u8(buf: &mut WriteBuffer, value: u8) -> Result<Marker, Error> {
    buf.write(2, |buf| {
        // Safety: 2 bytes available in buf.
        unsafe {
            let buf = buf.as_mut_ptr();
            *buf = Marker::U8.encoding();

            let buf = buf.add(1);
            *buf = *value.to_be_bytes().get_unchecked(0);
        }
    })?;

    Ok(Marker::U8)
}

/// Encode a 16-bit unsigned value.
pub fn write_u16(buf: &mut WriteBuffer, value: u16) -> Result<Marker, Error> {
    buf.write(3, |buf| {
        // Safety: 3 bytes available in buf.
        unsafe {
            let buf = buf.as_mut_ptr();
            *buf = Marker::U16.encoding();

            let buf = buf.add(1);
            buf.copy_from_nonoverlapping(u16::to_be_bytes(value).as_ptr(), 2);
        }
    })?;

    Ok(Marker::U16)
}

/// Encode a 32-bit unsigned value.
pub fn write_u32(buf: &mut WriteBuffer, value: u32) -> Result<Marker, Error> {
    buf.write(5, |buf| {
        // Safety: 5 bytes available in buf.
        unsafe {
            let buf = buf.as_mut_ptr();
            *buf = Marker::U32.encoding();

            let buf = buf.add(1);
            buf.copy_from_nonoverlapping(u32::to_be_bytes(value).as_ptr(), 4);
        }
    })?;

    Ok(Marker::U32)
}

/// Encode a 64-bit unsigned value.
pub fn write_u64(buf: &mut WriteBuffer, value: u64) -> Result<Marker, Error> {
    buf.write(9, |buf| {
        // Safety: 9 bytes available in buf.
        unsafe {
            let buf = buf.as_mut_ptr();
            *buf = Marker::U64.encoding();

            let buf = buf.add(1);
            buf.copy_from_nonoverlapping(u64::to_be_bytes(value).as_ptr(), 8);
        }
    })?;

    Ok(Marker::U64)
}
