use crate::{
    define::{TH_ESStreamInfo, *},
    helper::packet::{packet_final::CPacketViewRela, traits::AsCPacketView as _},
};

type RelaRef<'a> = Vec<CPacketViewRela<'a>>;

struct DataLifeTime<'a> {
    datas: Vec<*const u8>,
    datas_life_time: Vec<&'a [u8]>,
}

impl<'a> DataLifeTime<'a> {
    fn new() -> Self {
        Self {
            datas: Vec::new(),
            datas_life_time: Vec::new(),
        }
    }
    fn push(&mut self, data: &'a [u8]) {
        self.datas_life_time.push(data);
        self.datas.push(data.as_ptr());
    }

    fn as_dptr(&self) -> *const *const u8 {
        self.datas.as_ptr()
    }
}

pub struct ThEsstreamInfoGuard<'a> {
    // livetime
    #[allow(dead_code)]
    datas: DataLifeTime<'a>,
    #[allow(dead_code)]
    lens: Vec<usize>,
    #[allow(dead_code)]
    infos: Vec<&'a TH_ESStreamInfo>,
    //
    info: TH_ESStreamInfo,
}

impl<'a> ThEsstreamInfoGuard<'a> {
    fn new(relas: &'a RelaRef, base_info: &TH_ESStreamInfo) -> Self {
        let mut lens = Vec::with_capacity(relas.len());
        let mut infos: Vec<&'a TH_ESStreamInfo> = Vec::with_capacity(relas.len());
        let mut datas = DataLifeTime::new();
        for rela in relas.iter() {
            datas.push(rela.get_data());
            lens.push(rela.get_data().len());
            infos.push(rela.get_info());
        }
        let mut info = *base_info;
        info.rela_count = infos.len() as _;
        info.rela_info = infos.as_ptr() as _;
        info.rela_data = datas.as_dptr() as _;
        info.rela_data_len = lens.as_ptr() as _;

        ThEsstreamInfoGuard {
            datas,
            lens,
            infos,
            info,
        }
    }
    pub fn info(&self) -> &TH_ESStreamInfo {
        &self.info
    }
}
#[must_use = "Guard here"]
pub fn set_info_relas<'a>(base_info: &TH_ESStreamInfo, relas: &'a RelaRef) -> ThEsstreamInfoGuard<'a> {
    ThEsstreamInfoGuard::new(relas, base_info)
}
pub fn get_info_relas(info: &'_ TH_ESStreamInfo) -> RelaRef<'_> {
    let mut infos = Vec::new();
    if info.rela_count == 0 {
        return infos;
    }
    if info.rela_count > 10000 {
        tracing::warn!("Invalid rela packet, skip parse rela packet, info:{:?}", info);
        return infos;
    }

    for index in 0..info.rela_count {
        let index: isize = index as _;
        let info_ptr = unsafe { *(info.rela_info as *const *const TH_ESStreamInfo).offset(index) };

        let rela_data = unsafe {
            let ptr = *(info.rela_data as *const *const u8).offset(index);
            let len = *(info.rela_data_len as *const usize).offset(index);
            std::slice::from_raw_parts(ptr, len)
        };
        let rela = CPacketViewRela::new(rela_data, unsafe { &*info_ptr });

        infos.push(rela)
    }
    infos
}
pub fn clear_info_relas(info: &mut TH_ESStreamInfo) {
    info.rela_count = 0;
    info.rela_data = 0;
    info.rela_data_len = 0;
    info.rela_info = 0;
}

pub fn is_es_decodable_vcodec(info: &TH_ESStreamInfo) -> bool {
    info.vCodecType == TH_VIDEOCODECTYPE_TH_VCT_H265
        || info.vCodecType == TH_VIDEOCODECTYPE_TH_VCT_H264
        || info.vCodecType == TH_VIDEOCODECTYPE_TH_VCT_SVAC
        || info.vCodecType == 6
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_case_mix() {
        //
        let data1 = vec![1u8, 2, 3];
        let data2 = vec![4u8, 5, 6, 7];

        let rela_info1 = TH_ESStreamInfo {
            streamType: 1,
            ..Default::default()
        };
        let rela_info2 = TH_ESStreamInfo {
            streamType: 2,
            ..Default::default()
        };

        let rela_infos = vec![
            CPacketViewRela::new(&data1, &rela_info1),
            CPacketViewRela::new(&data2, &rela_info2),
        ];
        let base_info = TH_ESStreamInfo {
            streamType: 3,
            ..Default::default()
        };

        let guard = set_info_relas(&base_info, &rela_infos);

        {
            let out_info = get_info_relas(guard.info());

            assert_eq!(out_info.len(), 2);
            assert_eq!(out_info[0].stream_type(), 1);
            assert_eq!(out_info[0].as_slice(), [1, 2, 3]);

            assert_eq!(out_info[1].stream_type(), 2);
            assert_eq!(out_info[1].as_slice().len(), 4);
            assert_eq!(out_info[1].as_slice(), [4, 5, 6, 7]);
        }

        {
            let mut info_cloned = *guard.info();
            // clear info
            clear_info_relas(&mut info_cloned);
            let out_info = get_info_relas(&info_cloned);
            assert_eq!(out_info.len(), 0);
        }
    }
}
