#![cfg(feature = "std")]

use rmp::decode::{LenError, MessageLen};
use rmp::encode::*;
use rmp::Marker;

#[track_caller]
fn check_estimates(msg: &[u8], expected: &[i32]) {
    assert_eq!(msg.len() + 1, expected.len(), "off by {}", msg.len() as isize + 1 - expected.len() as isize);

    fn take_res(r: Result<usize, LenError>) -> (i32, usize) {
        match r {
            Err(r) => (r.len() as i32, r.len()),
            Ok(r) => (-(r as i32), r),
        }
    }

    let predicted = (0..=msg.len())
        .map(|partial_len| {
            let partial_msg = &msg[..partial_len];
            let (res, predicted) = take_res(MessageLen::len_of(partial_msg));
            assert!(predicted > partial_len.min(msg.len() - 1), "{predicted} > {partial_len}/{}", msg.len());
            res
        })
        .collect::<Vec<_>>();
    assert_eq!(expected, predicted, "quadratic");
    assert_eq!(msg.len(), MessageLen::len_of(msg).expect("complete message"));

    let mut incremental = MessageLen::with_limits(1024, 1<<16);
    let predicted = [&[][..]].into_iter().chain(msg.chunks(1)).map(|chunk| {
        let (res, _) = take_res(incremental.incremental_len(chunk));
        res
    }).collect::<Vec<_>>();
    assert_eq!(expected, predicted, "incremental");

    for frag_len in [1, 2, 3, 5, 7] {
        let mut incremental = MessageLen::with_limits(1024, msg.len());
        let predicted = [&[][..]].into_iter().chain(msg.chunks(frag_len)).map(|chunk| {
            match incremental.incremental_len(chunk) {
                Err(r) => r.len(),
                Ok(r) => r,
            }
        }).max().unwrap_or(usize::MAX);
        assert_eq!(msg.len(), predicted, "incremental {frag_len}");
    }
}

#[test]
fn array() {
    assert_eq!(1, MessageLen::len_of(&[]).unwrap_err().len());

    let mut out = [0u8; 1];
    write_bool(&mut out.as_mut_slice(), true).unwrap();
    assert_eq!(1, MessageLen::len_of(&out).unwrap());

    let mut out = Vec::new();
    write_array_len(&mut out, 4).unwrap();
    write_u16(&mut out, 333).unwrap();
    write_bool(&mut out, true).unwrap();
    write_u64(&mut out, 1 << 33).unwrap();
    write_bin_len(&mut out, 5).unwrap();
    out.extend(b"hello");

    check_estimates(&out, &[1, 5, 5, 5, 7, 7, 14, 14, 14, 14, 14, 14, 14, 14, 15, 16, 21, 21, 21, 21, 21, -21]);
}

#[test]
fn map() {
    let mut out = Vec::new();
    write_map_len(&mut out, 3).unwrap();
        write_u16(&mut out, 333).unwrap();
        write_bool(&mut out, true).unwrap();

        write_str_len(&mut out, 5).unwrap();
        out.extend(b"hello");
        write_nil(&mut out).unwrap();

        write_f64(&mut out, 1.23).unwrap();
        write_map_len(&mut out, 2).unwrap();
            write_nil(&mut out).unwrap();
            out.push(Marker::Array32.to_u8());
                out.extend_from_slice(&1u32.to_be_bytes());
                write_uint8(&mut out, 3).unwrap();
            write_nil(&mut out).unwrap();
            write_u32(&mut out, 1).unwrap();


    check_estimates(&out, &[1, 7, 7, 7, 9, 9, 11, 11, 11, 11, 11, 14, 14, 21, 21, 21, 21, 21, 21, 21, 21, 22, 26, 26, 28, 28, 28, 28, 29, 31, 31, 35, 35, 35, 35, -35]);
}

#[test]
fn nested() {
    let mut out = Vec::new();
    write_array_len(&mut out, 1).unwrap();
    write_array_len(&mut out, 1).unwrap();
    write_array_len(&mut out, 1).unwrap();
    out.push(Marker::Array32.to_u8());
    out.extend_from_slice(&1u32.to_be_bytes());
    write_array_len(&mut out, 1).unwrap();
    write_array_len(&mut out, 1).unwrap();
    write_array_len(&mut out, 1).unwrap();
    write_array_len(&mut out, 2).unwrap();
    write_array_len(&mut out, 1).unwrap();
    out.push(Marker::Array16.to_u8());
    out.extend_from_slice(&1u16.to_be_bytes());
    write_array_len(&mut out, 1).unwrap();
    write_array_len(&mut out, 1).unwrap();
    write_array_len(&mut out, 1).unwrap();
    write_array_len(&mut out, 1).unwrap();
    write_nil(&mut out).unwrap();
    write_nil(&mut out).unwrap();

    check_estimates(&out, &[1, 2, 3, 4, 8, 8, 8, 8, 9, 10, 11, 12, 14, 14, 16, 16, 17, 18, 19, 20, 21, 22, -22]);

    assert!(MessageLen::with_limits(4, 1 << 16).incremental_len(out.as_slice()).is_err());
    assert!(MessageLen::with_limits(14, 1 << 16).incremental_len(out.as_slice()).is_ok());
}
