use crate::*;

ext! {
    name: Tfhd,
    versions: [0],
    flags: {
        base_data_offset = 0,
        sample_description_index = 1,
        default_sample_duration = 3,
        default_sample_size = 4,
        default_sample_flags = 5,
        duration_is_empty = 16,
        default_base_is_moof = 17,
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Tfhd {
    pub track_id: u32,
    pub base_data_offset: Option<u64>,
    pub sample_description_index: Option<u32>,
    pub default_sample_duration: Option<u32>,
    pub default_sample_size: Option<u32>,
    pub default_sample_flags: Option<u32>,
    pub duration_is_empty: bool,
    pub default_base_is_moof: bool,
}

impl AtomExt for Tfhd {
    const KIND_EXT: FourCC = FourCC::new(b"tfhd");

    type Ext = TfhdExt;

    fn decode_body_ext<B: Buf>(buf: &mut B, ext: TfhdExt) -> Result<Self> {
        let track_id = u32::decode(buf)?;

        let base_data_offset = match ext.base_data_offset {
            true => u64::decode(buf)?.into(),
            false => None,
        };

        let sample_description_index = match ext.sample_description_index {
            true => u32::decode(buf)?.into(),
            false => None,
        };

        let default_sample_duration = match ext.default_sample_duration {
            true => u32::decode(buf)?.into(),
            false => None,
        };

        let default_sample_size = match ext.default_sample_size {
            true => u32::decode(buf)?.into(),
            false => None,
        };

        let default_sample_flags = match ext.default_sample_flags {
            true => u32::decode(buf)?.into(),
            false => None,
        };

        Ok(Tfhd {
            track_id,
            base_data_offset,
            sample_description_index,
            default_sample_duration,
            default_sample_size,
            default_sample_flags,
            duration_is_empty: ext.duration_is_empty,
            default_base_is_moof: ext.default_base_is_moof,
        })
    }

    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<TfhdExt> {
        let ext = TfhdExt {
            base_data_offset: self.base_data_offset.is_some(),
            sample_description_index: self.sample_description_index.is_some(),
            default_sample_duration: self.default_sample_duration.is_some(),
            default_sample_size: self.default_sample_size.is_some(),
            default_sample_flags: self.default_sample_flags.is_some(),
            duration_is_empty: self.duration_is_empty,
            default_base_is_moof: self.default_base_is_moof,
            ..Default::default()
        };

        self.track_id.encode(buf)?;
        self.base_data_offset.encode(buf)?;
        self.sample_description_index.encode(buf)?;
        self.default_sample_duration.encode(buf)?;
        self.default_sample_size.encode(buf)?;
        self.default_sample_flags.encode(buf)?;

        Ok(ext)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_tfhd() {
        let expected = Tfhd {
            track_id: 1,
            base_data_offset: None,
            sample_description_index: None,
            default_sample_duration: None,
            default_sample_size: None,
            default_sample_flags: None,
            duration_is_empty: false,
            default_base_is_moof: false,
        };
        let mut buf = Vec::new();
        expected.encode(&mut buf).unwrap();

        let mut buf = buf.as_ref();
        let decoded = Tfhd::decode(&mut buf).unwrap();
        assert_eq!(decoded, expected);
    }

    #[test]
    fn test_tfhd_with_flags() {
        let expected = Tfhd {
            track_id: 1,
            base_data_offset: None,
            sample_description_index: Some(1),
            default_sample_duration: Some(512),
            default_sample_size: None,
            default_sample_flags: Some(0x1010000),
            duration_is_empty: false,
            default_base_is_moof: false,
        };
        let mut buf = Vec::new();
        expected.encode(&mut buf).unwrap();

        let mut buf = buf.as_ref();
        let decoded = Tfhd::decode(&mut buf).unwrap();
        assert_eq!(decoded, expected);
    }

    #[test]
    fn test_tfhd_duration_is_empty() {
        let expected = Tfhd {
            track_id: 1,
            duration_is_empty: true,
            ..Default::default()
        };
        let mut buf = Vec::new();
        expected.encode(&mut buf).unwrap();

        let mut buf = buf.as_ref();
        let decoded = Tfhd::decode(&mut buf).unwrap();
        assert_eq!(decoded, expected);
    }

    #[test]
    fn test_tfhd_default_base_is_moof() {
        let expected = Tfhd {
            track_id: 1,
            default_base_is_moof: true,
            default_sample_duration: Some(512),
            default_sample_size: Some(0),
            default_sample_flags: Some(0x1010000),
            ..Default::default()
        };
        let mut buf = Vec::new();
        expected.encode(&mut buf).unwrap();

        let mut buf = buf.as_ref();
        let decoded = Tfhd::decode(&mut buf).unwrap();
        assert_eq!(decoded, expected);
    }
}
