use crate::{
    define::TH_ESStreamInfo,
    helper::packet::{
        helper::{clear_info_relas, get_info_relas, set_info_relas},
        packet_final::CPacketViewRela,
        packet_view::CPacketView,
        traits::AsCPacketView as _,
    },
};
use anyhow::Result;
use bytes::Bytes;

#[derive(Clone)]
pub struct PacketFinal {
    payload: Bytes,
    info: TH_ESStreamInfo,
}

impl PacketFinal {
    fn new(data: &[u8], info: TH_ESStreamInfo) -> Self {
        let payload = bytes::Bytes::copy_from_slice(data);
        Self { payload, info }
    }
    pub fn payload(&self) -> &[u8] {
        &self.payload
    }
    pub fn info(&self) -> &TH_ESStreamInfo {
        &self.info
    }
}
#[derive(Clone)]
pub struct Packet {
    core: PacketFinal,
    relas: Vec<PacketFinal>,
}

impl Packet {
    pub fn new(data: &[u8], info: &TH_ESStreamInfo) -> Self {
        let mut relas = Vec::new();
        let mut info = *info;

        for rela in get_info_relas(&info) {
            let rela_cloned = PacketFinal::new(rela.as_slice(), *rela.get_info());

            relas.push(rela_cloned);
        }
        clear_info_relas(&mut info);

        let core = PacketFinal::new(data, info);

        Self { core, relas }
    }
    pub fn rela(&self) -> &[PacketFinal] {
        &self.relas
    }
    pub fn core(&self) -> &PacketFinal {
        &self.core
    }
    pub fn inspect<'a, F, T>(&'a self, f: F) -> Result<T>
    where
        F: FnOnce(CPacketView) -> Result<T>,
    {
        let base_info = *&self.core.info;
        let mut v = Vec::new();
        for rela in &self.relas {
            let view = CPacketViewRela::new(rela.payload(), rela.info());
            v.push(view);
        }
        let guard = set_info_relas(&base_info, &v);

        f(CPacketView::new(self.core.payload(), guard.info()))
    }

    pub fn inspect_th_info<'a, F, T>(&'a self, f: F) -> Result<T>
    where
        F: FnOnce(&[u8], &TH_ESStreamInfo) -> Result<T>,
    {
        self.inspect(|packet_view| f(packet_view.as_slice(), packet_view.info()))
    }
}
impl<'a> From<&CPacketView<'a>> for Packet {
    fn from(value: &CPacketView) -> Self {
        Packet::new(value.as_slice(), value.info())
    }
}
impl<'a> From<CPacketView<'a>> for Packet {
    fn from(value: CPacketView) -> Self {
        Packet::from(&value)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_case_data_with_es() {
        let rela_data: Vec<u8> = vec![4, 5, 6];
        let rela_info = TH_ESStreamInfo {
            streamType: 1,
            ..Default::default()
        };

        let data = vec![1, 2, 3];
        let base_info = TH_ESStreamInfo {
            streamType: 6,
            ..Default::default()
        };

        let relas = vec![CPacketViewRela::new(&rela_data, &rela_info)];
        let guard = set_info_relas(&base_info, &relas);
        let packet = Packet::new(&data, guard.info());
        assert_eq!(packet.core().info().rela_count, 0); // rela info was clear
        assert_eq!(packet.core().info().rela_info, 0); // rela info was clear
        assert_eq!(packet.core().info().rela_data, 0); // rela info was clear
        assert_eq!(packet.core().info().rela_data_len, 0); // rela info was clear

        assert_eq!(packet.core().info().streamType, 6);
        assert_eq!(packet.core().payload(), [1, 2, 3]);
        assert_eq!(packet.rela().len(), 1);
        assert_eq!(packet.rela()[0].info().streamType, 1);
        assert_eq!(packet.rela()[0].payload(), [4, 5, 6]);
        // View th

        let result = packet.inspect_th_info(move |payload, th_info| {
            assert_eq!(payload, [1, 2, 3]);

            let rela_view = get_info_relas(th_info);
            assert_eq!(rela_view.len(), 1);
            assert_eq!(rela_view[0].stream_type(), 1);
            assert_eq!(rela_view[0].as_slice(), [4, 5, 6]);
            Ok(())
        });
        assert!(result.is_ok());

        // create from view
        {
            let view: CPacketView = CPacketView::new(&data, guard.info());
            let info = view.info();
            assert_eq!(info.streamType, 6);
            assert_eq!(view.as_slice(), [1, 2, 3]);
            assert_eq!(view.relas().len(), 1);
            assert_eq!(view.relas()[0].stream_type(), 1);
            assert_eq!(view.relas()[0].as_slice(), [4, 5, 6]);
        }

        // trait convert
        {
            //
            let view: CPacketView = CPacketView::new(&data, guard.info());
            let packet_from_ref = Packet::from(&view);
            assert_eq!(packet_from_ref.core().info().streamType, 6);
            assert_eq!(packet_from_ref.core().payload(), [1, 2, 3]);
            assert_eq!(packet_from_ref.rela().len(), 1);
            assert_eq!(packet_from_ref.rela()[0].info().streamType, 1);
            assert_eq!(packet_from_ref.rela()[0].payload(), [4, 5, 6]);
            //
            let packet_frezz = Packet::from(view);
            assert_eq!(packet_frezz.core().info().streamType, 6);
            assert_eq!(packet_frezz.core().payload(), [1, 2, 3]);
            assert_eq!(packet_frezz.rela().len(), 1);
            assert_eq!(packet_frezz.rela()[0].info().streamType, 1);
            assert_eq!(packet_frezz.rela()[0].payload(), [4, 5, 6]);
        }
    }
}
