use std::time::Instant;

use str0m::media::{MediaKind, MediaTime};
use str0m::rtp::vla::{ResolutionAndFramerate, Serializer as VlaSerializer};
use str0m::rtp::vla::{SimulcastStreamAllocation, SpatialLayerAllocation};
use str0m::rtp::vla::{TemporalLayerAllocation, URI as VLA_URI, VideoLayersAllocation};
use str0m::rtp::{Extension, ExtensionValues, Ssrc};
use str0m::{Event, Rtc, RtcError};

mod common;
use common::{connect_l_r_with_rtc, init_crypto_default, init_log, progress};

#[test]
pub fn vla_rtp_mode() -> Result<(), RtcError> {
    init_log();
    init_crypto_default();

    let vla_ext = Extension::with_serializer(VLA_URI, VlaSerializer);

    let now = Instant::now();
    let rtc_l = Rtc::builder()
        .set_rtp_mode(true)
        .set_extension(14, vla_ext.clone())
        .build(now);
    let rtc_r = Rtc::builder()
        .set_rtp_mode(true)
        .set_extension(14, vla_ext)
        .build(now);

    let (mut l, mut r) = connect_l_r_with_rtc(rtc_l, rtc_r);

    let mid = "vid".into();
    let ssrc_tx: Ssrc = 1000.into();

    l.direct_api().declare_media(mid, MediaKind::Video);
    l.direct_api().declare_stream_tx(ssrc_tx, None, mid, None);
    r.direct_api().declare_media(mid, MediaKind::Video);

    let max = l.last.max(r.last);
    l.last = max;
    r.last = max;

    let params = l.params_vp8();
    let pt = params.pt();
    let ssrc = l.direct_api().stream_tx_by_mid(mid, None).unwrap().ssrc();

    let vla = VideoLayersAllocation {
        current_simulcast_stream_index: 0,
        simulcast_streams: vec![SimulcastStreamAllocation {
            spatial_layers: vec![SpatialLayerAllocation {
                temporal_layers: vec![TemporalLayerAllocation {
                    cumulative_kbps: 500,
                }],
                resolution_and_framerate: Some(ResolutionAndFramerate {
                    width: 640,
                    height: 480,
                    framerate: 30,
                }),
            }],
        }],
    };

    let mut exts = ExtensionValues::default();
    exts.mid = Some(mid);
    exts.user_values.set(vla.clone());

    let last = l.last;
    let mut direct = l.direct_api();
    let stream = direct.stream_tx(&ssrc).unwrap();
    stream
        .write_rtp(
            pt,
            (1000 as u64).into(),
            0,
            last,
            false,
            exts,
            false,
            vec![0xAA; 10],
        )
        .unwrap();

    let mut vla_received = false;
    for _ in 0..100 {
        progress(&mut l, &mut r)?;

        for (_, event) in &r.events {
            if let Event::RtpPacket(packet) = event {
                if let Some(v) = packet
                    .header
                    .ext_vals
                    .user_values
                    .get::<VideoLayersAllocation>()
                {
                    assert_eq!(v, &vla);
                    assert_eq!(
                        v.simulcast_streams[0].spatial_layers[0].temporal_layers[0].cumulative_kbps,
                        500
                    );
                    assert_eq!(
                        v.simulcast_streams[0].spatial_layers[0]
                            .resolution_and_framerate
                            .as_ref()
                            .unwrap()
                            .width,
                        640
                    );
                    vla_received = true;
                } else {
                    println!(
                        "Packet received but no VLA. Exts: {:?}",
                        packet.header.ext_vals
                    );
                }
            }
        }
        if vla_received {
            break;
        }
        r.events.clear();
    }

    assert!(vla_received);

    Ok(())
}

#[test]
pub fn vla_frame_mode() -> Result<(), RtcError> {
    init_log();
    init_crypto_default();

    let vla_ext = Extension::with_serializer(VLA_URI, VlaSerializer);

    let now = Instant::now();
    let rtc_l = Rtc::builder().set_extension(14, vla_ext.clone()).build(now);
    let rtc_r = Rtc::builder().set_extension(14, vla_ext).build(now);

    let (mut l, mut r) = connect_l_r_with_rtc(rtc_l, rtc_r);

    let mid = "vid".into();
    let ssrc_tx: Ssrc = 1000.into();

    l.direct_api().declare_media(mid, MediaKind::Video);
    l.direct_api().declare_stream_tx(ssrc_tx, None, mid, None);
    r.direct_api().declare_media(mid, MediaKind::Video);

    let max = l.last.max(r.last);
    l.last = max;
    r.last = max;

    let vla = VideoLayersAllocation {
        current_simulcast_stream_index: 0,
        simulcast_streams: vec![SimulcastStreamAllocation {
            spatial_layers: vec![SpatialLayerAllocation {
                temporal_layers: vec![TemporalLayerAllocation {
                    cumulative_kbps: 500,
                }],
                resolution_and_framerate: Some(ResolutionAndFramerate {
                    width: 640,
                    height: 480,
                    framerate: 30,
                }),
            }],
        }],
    };

    let pt = l.params_vp8().pt();
    let last = l.last;

    let writer = l.writer(mid).unwrap();

    writer
        .user_extension_value(vla.clone())
        .write(pt, last, MediaTime::ZERO, vec![0xAA; 10])?;

    let mut vla_received = false;
    for _ in 0..100 {
        progress(&mut l, &mut r)?;

        for (_, event) in &r.events {
            if let Event::MediaData(data) = event {
                if let Some(v) = data.ext_vals.user_values.get::<VideoLayersAllocation>() {
                    assert_eq!(v, &vla);
                    assert_eq!(
                        v.simulcast_streams[0].spatial_layers[0].temporal_layers[0].cumulative_kbps,
                        500
                    );
                    assert_eq!(
                        v.simulcast_streams[0].spatial_layers[0]
                            .resolution_and_framerate
                            .as_ref()
                            .unwrap()
                            .width,
                        640
                    );
                    vla_received = true;
                } else {
                    println!("MediaData received but no VLA. Exts: {:?}", data.ext_vals);
                }
            }
        }
        if vla_received {
            break;
        }
        r.events.clear();
    }

    assert!(vla_received);

    Ok(())
}
