use crate::{Error, MlsDecode, MlsEncode, MlsSize};
use alloc::vec::Vec;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct VarInt(pub u32);
impl VarInt {
    pub const MAX: VarInt = VarInt((1 << 30) - 1);
}
impl From<VarInt> for u32 {
    #[inline]
    fn from(n: VarInt) -> u32 {
        n.0
    }
}
impl TryFrom<u32> for VarInt {
    type Error = Error;
    fn try_from(n: u32) -> Result<Self, Error> {
        (n <= u32::from(VarInt::MAX))
            .then_some(VarInt(n))
            .ok_or(Error::VarIntOutOfRange)
    }
}
impl TryFrom<usize> for VarInt {
    type Error = Error;
    fn try_from(n: usize) -> Result<Self, Error> {
        u32::try_from(n)
            .map_err(|_| Error::VarIntOutOfRange)?
            .try_into()
    }
}
impl MlsSize for VarInt {
    #[inline]
    fn mls_encoded_len(&self) -> usize {
        count_bytes_to_encode_int(*self) as usize
    }
}
impl MlsEncode for VarInt {
    fn mls_encode(&self, writer: &mut Vec<u8>) -> Result<(), Error> {
        let mut bytes = self.0.to_be_bytes();
        let bytes = match count_bytes_to_encode_int(*self) {
            LengthEncoding::One => &bytes[3..],
            LengthEncoding::Two => {
                bytes[2] |= 0x40;
                &bytes[2..]
            }
            LengthEncoding::Four => {
                bytes[0] |= 0x80;
                &bytes
            }
        };
        writer.extend_from_slice(bytes);
        Ok(())
    }
}
impl MlsDecode for VarInt {
    fn mls_decode(reader: &mut &[u8]) -> Result<Self, Error> {
        let first = u8::mls_decode(reader)?;
        let prefix = first >> 6;
        let count = (prefix < 3)
            .then_some(1 << prefix)
            .ok_or(Error::InvalidVarIntPrefix(prefix))?;
        let n = (1..count).try_fold(u32::from(first & 0x3f), |n, _| {
            u8::mls_decode(reader).map(|b| n << 8 | u32::from(b))
        })?;
        let n = VarInt(n);
        if count_bytes_to_encode_int(n) as usize == count {
            Ok(n)
        } else {
            Err(Error::VarIntMinimumLengthEncoding)
        }
    }
}
#[derive(Debug)]
enum LengthEncoding {
    One = 1,
    Two = 2,
    Four = 4,
}
fn count_bytes_to_encode_int(n: VarInt) -> LengthEncoding {
    let used_bits = 32 - n.0.leading_zeros();
    match used_bits {
        0..=6 => LengthEncoding::One,
        7..=14 => LengthEncoding::Two,
        15..=30 => LengthEncoding::Four,
        _ => panic!("Such a large VarInt cannot be instantiated"),
    }
}
#[cfg(test)]
mod tests {
    use super::VarInt;
    use crate::{Error, MlsDecode, MlsEncode};
    use assert_matches::assert_matches;
    #[cfg(target_arch = "wasm32")]
    use wasm_bindgen_test::wasm_bindgen_test as test;
    #[test]
    fn zero_is_convertible_to_varint() {
        assert_matches!(VarInt::try_from(0u32).map(u32::from), Ok(0));
    }
    #[test]
    fn successor_of_max_varint_is_not_convertible_to_varint() {
        let n = u32::from(VarInt::MAX) + 1;
        assert_matches!(VarInt::try_from(n), Err(Error::VarIntOutOfRange));
    }
    #[test]
    fn zero_serializes_as_single_null_byte() {
        assert_eq!(
            VarInt::try_from(0u32).unwrap().mls_encode_to_vec().unwrap(),
            [0]
        );
    }
    #[test]
    fn zero_roundtrips() {
        let n = VarInt::try_from(0u32).unwrap();
        let serialized = n.mls_encode_to_vec().unwrap();
        let restored = VarInt::mls_decode(&mut &*serialized).unwrap();
        assert_eq!(restored, n);
    }
    #[test]
    fn varint_max_roundtrips() {
        let n = VarInt::MAX;
        let serialized = n.mls_encode_to_vec().unwrap();
        let restored = VarInt::mls_decode(&mut &*serialized).unwrap();
        assert_eq!(restored, n);
    }
    fn decoding_matches_rfc(encoded: u32, decoded: u32) {
        let bytes = encoded.to_be_bytes();
        let start = bytes
            .iter()
            .position(|&b| b != 0)
            .unwrap_or(bytes.len() - 1);
        let bytes = &bytes[start..];
        assert_eq!(
            VarInt::mls_decode(&mut &*bytes).unwrap(),
            VarInt::try_from(decoded).unwrap()
        );
    }
    #[test]
    fn decoding_0x25_matches_rfc_result() {
        decoding_matches_rfc(0x25, 37);
    }
    #[test]
    fn decoding_0x7bbd_matches_rfc_result() {
        decoding_matches_rfc(0x7bbd, 15293);
    }
    #[test]
    fn decoding_0x9d7f3e7d_matches_rfc_result() {
        decoding_matches_rfc(0x9d7f3e7d, 494878333);
    }
}