// https://tools.ietf.org/html/rfc8216#section-8
use std::convert::TryFrom;
use std::time::Duration;

use hls_m3u8::tags::{ExtInf, ExtXKey, ExtXMedia, VariantStream};
use hls_m3u8::types::{DecryptionKey, EncryptionMethod, MediaType, StreamData};
use hls_m3u8::{MasterPlaylist, MediaPlaylist, MediaSegment};
use pretty_assertions::assert_eq;

macro_rules! generate_tests {
    ( $( $fnname:ident => { $struct:expr, $str:expr }),+ $(,)* ) => {
        $(
            #[test]
            fn $fnname() {
                assert_eq!($struct, TryFrom::try_from($str).unwrap());

                assert_eq!($struct.to_string(), $str.to_string());
            }
        )+
    }
}

generate_tests! {
    test_simple_playlist => {
        MediaPlaylist::builder()
            .target_duration(Duration::from_secs(10))
            .segments(vec![
                MediaSegment::builder()
                    .duration(ExtInf::new(Duration::from_secs_f64(9.009)))
                    .uri("http://media.example.com/first.ts")
                    .build()
                    .unwrap(),
                MediaSegment::builder()
                    .duration(ExtInf::new(Duration::from_secs_f64(9.009)))
                    .uri("http://media.example.com/second.ts")
                    .build()
                    .unwrap(),
                MediaSegment::builder()
                    .duration(ExtInf::new(Duration::from_secs_f64(3.003)))
                    .uri("http://media.example.com/third.ts")
                    .build()
                    .unwrap(),
            ])
            .has_end_list(true)
            .build()
            .unwrap(),
        concat!(
            "#EXTM3U\n",
            "#EXT-X-VERSION:3\n",
            "#EXT-X-TARGETDURATION:10\n",
            "#EXTINF:9.009,\n",
            "http://media.example.com/first.ts\n",
            "#EXTINF:9.009,\n",
            "http://media.example.com/second.ts\n",
            "#EXTINF:3.003,\n",
            "http://media.example.com/third.ts\n",
            "#EXT-X-ENDLIST\n"
        )
    },
    test_live_media_playlist_using_https => {
        MediaPlaylist::builder()
            .target_duration(Duration::from_secs(8))
            .media_sequence(2680)
            .segments(vec![
                MediaSegment::builder()
                    .duration(ExtInf::new(Duration::from_secs_f64(7.975)))
                    .uri("https://priv.example.com/fileSequence2680.ts")
                    .build()
                    .unwrap(),
                MediaSegment::builder()
                    .duration(ExtInf::new(Duration::from_secs_f64(7.941)))
                    .uri("https://priv.example.com/fileSequence2681.ts")
                    .build()
                    .unwrap(),
                MediaSegment::builder()
                    .duration(ExtInf::new(Duration::from_secs_f64(7.975)))
                    .uri("https://priv.example.com/fileSequence2682.ts")
                    .build()
                    .unwrap(),
            ])
            .build()
            .unwrap(),
        concat!(
            "#EXTM3U\n",
            "#EXT-X-VERSION:3\n",
            "#EXT-X-TARGETDURATION:8\n",
            "#EXT-X-MEDIA-SEQUENCE:2680\n",
            "#EXTINF:7.975,\n",
            "https://priv.example.com/fileSequence2680.ts\n",
            "#EXTINF:7.941,\n",
            "https://priv.example.com/fileSequence2681.ts\n",
            "#EXTINF:7.975,\n",
            "https://priv.example.com/fileSequence2682.ts\n",
        )
    },
    test_media_playlist_with_encrypted_segments => {
        MediaPlaylist::builder()
            .target_duration(Duration::from_secs(15))
            .media_sequence(7794)
            .segments(vec![
                MediaSegment::builder()
                    .duration(ExtInf::new(Duration::from_secs_f64(2.833)))
                    .keys(vec![
                        ExtXKey::new(DecryptionKey::new(
                            EncryptionMethod::Aes128,
                            "https://priv.example.com/key.php?r=52"
                        ))
                    ])
                    .uri("http://media.example.com/fileSequence52-A.ts")
                    .build()
                    .unwrap(),
                MediaSegment::builder()
                    .duration(ExtInf::new(Duration::from_secs_f64(15.0)))
                    .keys(vec![
                        ExtXKey::new(DecryptionKey::new(
                            EncryptionMethod::Aes128,
                            "https://priv.example.com/key.php?r=52"
                        ))
                    ])
                    .uri("http://media.example.com/fileSequence52-B.ts")
                    .build()
                    .unwrap(),
                MediaSegment::builder()
                    .duration(ExtInf::new(Duration::from_secs_f64(13.333)))
                    .keys(vec![
                        ExtXKey::new(DecryptionKey::new(
                            EncryptionMethod::Aes128,
                            "https://priv.example.com/key.php?r=52"
                        ))
                    ])
                    .uri("http://media.example.com/fileSequence52-C.ts")
                    .build()
                    .unwrap(),
                MediaSegment::builder()
                    .duration(ExtInf::new(Duration::from_secs_f64(15.0)))
                    .keys(vec![
                        ExtXKey::new(DecryptionKey::new(
                            EncryptionMethod::Aes128,
                            "https://priv.example.com/key.php?r=53"
                        ))
                    ])
                    .uri("http://media.example.com/fileSequence53-A.ts")
                    .build()
                    .unwrap(),
            ])
            .build()
            .unwrap(),
        concat!(
            "#EXTM3U\n",
            "#EXT-X-VERSION:3\n",
            "#EXT-X-TARGETDURATION:15\n",
            "#EXT-X-MEDIA-SEQUENCE:7794\n",

            "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=52\"\n",

            "#EXTINF:2.833,\n",
            "http://media.example.com/fileSequence52-A.ts\n",
            "#EXTINF:15,\n",
            "http://media.example.com/fileSequence52-B.ts\n",
            "#EXTINF:13.333,\n",
            "http://media.example.com/fileSequence52-C.ts\n",

            "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=53\"\n",

            "#EXTINF:15,\n",
            "http://media.example.com/fileSequence53-A.ts\n"
        )
    },
    test_master_playlist => {
        MasterPlaylist::builder()
            .variant_streams(vec![
                VariantStream::ExtXStreamInf {
                    uri: "http://example.com/low.m3u8".into(),
                    frame_rate: None,
                    audio: None,
                    subtitles: None,
                    closed_captions: None,
                    stream_data: StreamData::builder()
                        .bandwidth(1280000)
                        .average_bandwidth(1000000)
                        .build()
                        .unwrap()
                },
                VariantStream::ExtXStreamInf {
                    uri: "http://example.com/mid.m3u8".into(),
                    frame_rate: None,
                    audio: None,
                    subtitles: None,
                    closed_captions: None,
                    stream_data: StreamData::builder()
                        .bandwidth(2560000)
                        .average_bandwidth(2000000)
                        .build()
                        .unwrap()
                },
                VariantStream::ExtXStreamInf {
                    uri: "http://example.com/hi.m3u8".into(),
                    frame_rate: None,
                    audio: None,
                    subtitles: None,
                    closed_captions: None,
                    stream_data: StreamData::builder()
                        .bandwidth(7680000)
                        .average_bandwidth(6000000)
                        .build()
                        .unwrap()
                },
                VariantStream::ExtXStreamInf {
                    uri: "http://example.com/audio-only.m3u8".into(),
                    frame_rate: None,
                    audio: None,
                    subtitles: None,
                    closed_captions: None,
                    stream_data: StreamData::builder()
                        .bandwidth(65000)
                        .codecs(&["mp4a.40.5"])
                        .build()
                        .unwrap()
                },
            ])
            .build()
            .unwrap(),
        concat!(
            "#EXTM3U\n",
            "#EXT-X-STREAM-INF:BANDWIDTH=1280000,AVERAGE-BANDWIDTH=1000000\n",
            "http://example.com/low.m3u8\n",
            "#EXT-X-STREAM-INF:BANDWIDTH=2560000,AVERAGE-BANDWIDTH=2000000\n",
            "http://example.com/mid.m3u8\n",
            "#EXT-X-STREAM-INF:BANDWIDTH=7680000,AVERAGE-BANDWIDTH=6000000\n",
            "http://example.com/hi.m3u8\n",
            "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n",
            "http://example.com/audio-only.m3u8\n"
        )
    },
    test_master_playlist_with_i_frames => {
        MasterPlaylist::builder()
            .variant_streams(vec![
                VariantStream::ExtXStreamInf {
                    uri: "low/audio-video.m3u8".into(),
                    frame_rate: None,
                    audio: None,
                    subtitles: None,
                    closed_captions: None,
                    stream_data: StreamData::new(1280000)
                },
                VariantStream::ExtXIFrame {
                    uri: "low/iframe.m3u8".into(),
                    stream_data: StreamData::new(86000),
                },
                VariantStream::ExtXStreamInf {
                    uri: "mid/audio-video.m3u8".into(),
                    frame_rate: None,
                    audio: None,
                    subtitles: None,
                    closed_captions: None,
                    stream_data: StreamData::new(2560000)
                },
                VariantStream::ExtXIFrame {
                    uri: "mid/iframe.m3u8".into(),
                    stream_data: StreamData::new(150000),
                },
                VariantStream::ExtXStreamInf {
                    uri: "hi/audio-video.m3u8".into(),
                    frame_rate: None,
                    audio: None,
                    subtitles: None,
                    closed_captions: None,
                    stream_data: StreamData::new(7680000)
                },
                VariantStream::ExtXIFrame {
                    uri: "hi/iframe.m3u8".into(),
                    stream_data: StreamData::new(550000),
                },
                VariantStream::ExtXStreamInf {
                    uri: "audio-only.m3u8".into(),
                    frame_rate: None,
                    audio: None,
                    subtitles: None,
                    closed_captions: None,
                    stream_data: StreamData::builder()
                        .bandwidth(65000)
                        .codecs(&["mp4a.40.5"])
                        .build()
                        .unwrap()
                },
            ])
            .build()
            .unwrap(),
        concat!(
            "#EXTM3U\n",
            "#EXT-X-STREAM-INF:BANDWIDTH=1280000\n",
            "low/audio-video.m3u8\n",
            "#EXT-X-I-FRAME-STREAM-INF:URI=\"low/iframe.m3u8\",BANDWIDTH=86000\n",
            "#EXT-X-STREAM-INF:BANDWIDTH=2560000\n",
            "mid/audio-video.m3u8\n",
            "#EXT-X-I-FRAME-STREAM-INF:URI=\"mid/iframe.m3u8\",BANDWIDTH=150000\n",
            "#EXT-X-STREAM-INF:BANDWIDTH=7680000\n",
            "hi/audio-video.m3u8\n",
            "#EXT-X-I-FRAME-STREAM-INF:URI=\"hi/iframe.m3u8\",BANDWIDTH=550000\n",
            "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n",
            "audio-only.m3u8\n"
        )
    },
    test_master_playlist_with_alternative_audio => {
        MasterPlaylist::builder()
            .media(vec![
                ExtXMedia::builder()
                    .media_type(MediaType::Audio)
                    .group_id("aac")
                    .name("English")
                    .is_default(true)
                    .is_autoselect(true)
                    .language("en")
                    .uri("main/english-audio.m3u8")
                    .build()
                    .unwrap(),
                ExtXMedia::builder()
                    .media_type(MediaType::Audio)
                    .group_id("aac")
                    .name("Deutsch")
                    .is_default(false)
                    .is_autoselect(true)
                    .language("de")
                    .uri("main/german-audio.m3u8")
                    .build()
                    .unwrap(),
                ExtXMedia::builder()
                    .media_type(MediaType::Audio)
                    .group_id("aac")
                    .name("Commentary")
                    .is_default(false)
                    .is_autoselect(false)
                    .language("en")
                    .uri("commentary/audio-only.m3u8")
                    .build()
                    .unwrap(),
            ])
            .variant_streams(vec![
                VariantStream::ExtXStreamInf {
                    uri: "low/video-only.m3u8".into(),
                    frame_rate: None,
                    audio: Some("aac".into()),
                    subtitles: None,
                    closed_captions: None,
                    stream_data: StreamData::builder()
                        .bandwidth(1280000)
                        .codecs(&["..."])
                        .build()
                        .unwrap()
                },
                VariantStream::ExtXStreamInf {
                    uri: "mid/video-only.m3u8".into(),
                    frame_rate: None,
                    audio: Some("aac".into()),
                    subtitles: None,
                    closed_captions: None,
                    stream_data: StreamData::builder()
                        .bandwidth(2560000)
                        .codecs(&["..."])
                        .build()
                        .unwrap()
                },
                VariantStream::ExtXStreamInf {
                    uri: "hi/video-only.m3u8".into(),
                    frame_rate: None,
                    audio: Some("aac".into()),
                    subtitles: None,
                    closed_captions: None,
                    stream_data: StreamData::builder()
                        .bandwidth(7680000)
                        .codecs(&["..."])
                        .build()
                        .unwrap()
                },
                VariantStream::ExtXStreamInf {
                    uri: "main/english-audio.m3u8".into(),
                    frame_rate: None,
                    audio: Some("aac".into()),
                    subtitles: None,
                    closed_captions: None,
                    stream_data: StreamData::builder()
                        .bandwidth(65000)
                        .codecs(&["mp4a.40.5"])
                        .build()
                        .unwrap()
                },
            ])
            .build()
            .unwrap(),
        concat!(
            "#EXTM3U\n",
            "#EXT-X-MEDIA:",
            "TYPE=AUDIO,",
            "URI=\"main/english-audio.m3u8\",",
            "GROUP-ID=\"aac\",",
            "LANGUAGE=\"en\",",
            "NAME=\"English\",",
            "DEFAULT=YES,",
            "AUTOSELECT=YES\n",

            "#EXT-X-MEDIA:",
            "TYPE=AUDIO,",
            "URI=\"main/german-audio.m3u8\",",
            "GROUP-ID=\"aac\",",
            "LANGUAGE=\"de\",",
            "NAME=\"Deutsch\",",
            "AUTOSELECT=YES\n",

            "#EXT-X-MEDIA:",
            "TYPE=AUDIO,",
            "URI=\"commentary/audio-only.m3u8\",",
            "GROUP-ID=\"aac\",",
            "LANGUAGE=\"en\",",
            "NAME=\"Commentary\"\n",

            "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"...\",AUDIO=\"aac\"\n",
            "low/video-only.m3u8\n",

            "#EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS=\"...\",AUDIO=\"aac\"\n",
            "mid/video-only.m3u8\n",

            "#EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS=\"...\",AUDIO=\"aac\"\n",
            "hi/video-only.m3u8\n",

            "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\",AUDIO=\"aac\"\n",
            "main/english-audio.m3u8\n"
        )
    },
    test_master_playlist_with_alternative_video => {
        MasterPlaylist::builder()
            .media(vec![
                // low
                ExtXMedia::builder()
                    .media_type(MediaType::Video)
                    .group_id("low")
                    .name("Main")
                    .is_default(true)
                    .is_autoselect(true)
                    .uri("low/main/audio-video.m3u8")
                    .build()
                    .unwrap(),
                ExtXMedia::builder()
                    .media_type(MediaType::Video)
                    .group_id("low")
                    .name("Centerfield")
                    .is_default(false)
                    .uri("low/centerfield/audio-video.m3u8")
                    .build()
                    .unwrap(),
                ExtXMedia::builder()
                    .media_type(MediaType::Video)
                    .group_id("low")
                    .name("Dugout")
                    .is_default(false)
                    .uri("low/dugout/audio-video.m3u8")
                    .build()
                    .unwrap(),
                // mid
                ExtXMedia::builder()
                    .media_type(MediaType::Video)
                    .group_id("mid")
                    .name("Main")
                    .is_default(true)
                    .is_autoselect(true)
                    .uri("mid/main/audio-video.m3u8")
                    .build()
                    .unwrap(),
                ExtXMedia::builder()
                    .media_type(MediaType::Video)
                    .group_id("mid")
                    .name("Centerfield")
                    .is_default(false)
                    .uri("mid/centerfield/audio-video.m3u8")
                    .build()
                    .unwrap(),
                ExtXMedia::builder()
                    .media_type(MediaType::Video)
                    .group_id("mid")
                    .name("Dugout")
                    .is_default(false)
                    .uri("mid/dugout/audio-video.m3u8")
                    .build()
                    .unwrap(),
                // hi
                ExtXMedia::builder()
                    .media_type(MediaType::Video)
                    .group_id("hi")
                    .name("Main")
                    .is_default(true)
                    .is_autoselect(true)
                    .uri("hi/main/audio-video.m3u8")
                    .build()
                    .unwrap(),
                ExtXMedia::builder()
                    .media_type(MediaType::Video)
                    .group_id("hi")
                    .name("Centerfield")
                    .is_default(false)
                    .uri("hi/centerfield/audio-video.m3u8")
                    .build()
                    .unwrap(),
                ExtXMedia::builder()
                    .media_type(MediaType::Video)
                    .group_id("hi")
                    .name("Dugout")
                    .is_default(false)
                    .uri("hi/dugout/audio-video.m3u8")
                    .build()
                    .unwrap(),
            ])
            .variant_streams(vec![
                VariantStream::ExtXStreamInf {
                    uri: "low/main/audio-video.m3u8".into(),
                    frame_rate: None,
                    audio: None,
                    subtitles: None,
                    closed_captions: None,
                    stream_data: StreamData::builder()
                        .bandwidth(1280000)
                        .codecs(&["..."])
                        .video("low")
                        .build()
                        .unwrap()
                },
                VariantStream::ExtXStreamInf {
                    uri: "mid/main/audio-video.m3u8".into(),
                    frame_rate: None,
                    audio: None,
                    subtitles: None,
                    closed_captions: None,
                    stream_data: StreamData::builder()
                        .bandwidth(2560000)
                        .codecs(&["..."])
                        .video("mid")
                        .build()
                        .unwrap()
                },
                VariantStream::ExtXStreamInf {
                    uri: "hi/main/audio-video.m3u8".into(),
                    frame_rate: None,
                    audio: None,
                    subtitles: None,
                    closed_captions: None,
                    stream_data: StreamData::builder()
                        .bandwidth(7680000)
                        .codecs(&["..."])
                        .video("hi")
                        .build()
                        .unwrap()
                },
            ])
            .build()
            .unwrap(),
        concat!(
            "#EXTM3U\n",
            "#EXT-X-MEDIA:",
            "TYPE=VIDEO,",
            "URI=\"low/main/audio-video.m3u8\",",
            "GROUP-ID=\"low\",",
            "NAME=\"Main\",",
            "DEFAULT=YES,",
            "AUTOSELECT=YES",
            "\n",

            "#EXT-X-MEDIA:",
            "TYPE=VIDEO,",
            "URI=\"low/centerfield/audio-video.m3u8\",",
            "GROUP-ID=\"low\",",
            "NAME=\"Centerfield\"",
            "\n",

            "#EXT-X-MEDIA:",
            "TYPE=VIDEO,",
            "URI=\"low/dugout/audio-video.m3u8\",",
            "GROUP-ID=\"low\",",
            "NAME=\"Dugout\"",
            "\n",


            "#EXT-X-MEDIA:",
            "TYPE=VIDEO,",
            "URI=\"mid/main/audio-video.m3u8\",",
            "GROUP-ID=\"mid\",",
            "NAME=\"Main\",",
            "DEFAULT=YES,",
            "AUTOSELECT=YES\n",

            "#EXT-X-MEDIA:",
            "TYPE=VIDEO,",
            "URI=\"mid/centerfield/audio-video.m3u8\",",
            "GROUP-ID=\"mid\",",
            "NAME=\"Centerfield\"\n",

            "#EXT-X-MEDIA:",
            "TYPE=VIDEO,",
            "URI=\"mid/dugout/audio-video.m3u8\",",
            "GROUP-ID=\"mid\",",
            "NAME=\"Dugout\"\n",

            "#EXT-X-MEDIA:",
            "TYPE=VIDEO,",
            "URI=\"hi/main/audio-video.m3u8\",",
            "GROUP-ID=\"hi\",",
            "NAME=\"Main\",",
            "DEFAULT=YES,",
            "AUTOSELECT=YES\n",

            "#EXT-X-MEDIA:",
            "TYPE=VIDEO,",
            "URI=\"hi/centerfield/audio-video.m3u8\",",
            "GROUP-ID=\"hi\",",
            "NAME=\"Centerfield\"\n",

            "#EXT-X-MEDIA:",
            "TYPE=VIDEO,",
            "URI=\"hi/dugout/audio-video.m3u8\",",
            "GROUP-ID=\"hi\",",
            "NAME=\"Dugout\"\n",

            "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"...\",VIDEO=\"low\"\n",
            "low/main/audio-video.m3u8\n",

            "#EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS=\"...\",VIDEO=\"mid\"\n",
            "mid/main/audio-video.m3u8\n",

            "#EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS=\"...\",VIDEO=\"hi\"\n",
            "hi/main/audio-video.m3u8\n",
        )
    }
}
