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

/// Encode the length of a map.
///
/// This function encodes the marker with the most efficient map length representation, based on `len`.
/// If `len` is [`None`], a flexible map marker is encoded.
/// In that case, the user must make sure that the map is properly terminated with a call to [`write_terminator`](crate::encode::write_terminator).
pub fn write_map(buf: &mut WriteBuffer, len: Option<usize>) -> Result<Marker, Error> {
    let Some(len) = len else {
        buf.write(1, |buf| buf[0] = Marker::MFlex.encoding())?;
        return Ok(Marker::MFlex);
    };

    let (marker, bytes): (_, &[u8]) = if len <= 0x0f {
        (Marker::ImmMap { len: len as _ }, &[])
    } else if len < u8::MAX as _ {
        (Marker::M8, &u8::to_be_bytes(len as _))
    } else if len < u16::MAX as _ {
        (Marker::M16, &u16::to_be_bytes(len as _))
    } else if len < u32::MAX as _ {
        (Marker::M32, &u32::to_be_bytes(len as _))
    } else {
        return Err(Error::OutOfRange);
    };

    buf.write(1 + bytes.len(), |buf| {
        // Safety: bytes.len() + 1 bytes available in buf.
        unsafe {
            let buf = buf.as_mut_ptr();
            *buf = marker.encoding();

            let buf = buf.add(1);
            buf.copy_from_nonoverlapping(bytes.as_ptr(), bytes.len());
        }
    })?;

    Ok(marker)
}
