use crate::{define::CESStreamInfo, sys::*, util::SlicePack};
use cyberex::{void::*, xself::*};
use std::{ffi::CString, marker::PhantomPinned, pin::Pin};

use anyhow::{bail, Result};

unsafe extern "C" fn comCb(
    data: *const ::std::os::raw::c_uchar,
    len: ::std::os::raw::c_int,
    user: *mut ::std::os::raw::c_void,
) {
    let p = opacue_to_mut::<TsMuxer>(user.cast());

    p.tsBuf.push(SlicePack {
        ptr: HyVoidConst::from_ptr(data.cast()),
        size: len as usize,
    });
}
pub struct TsMuxer {
    ts: HyVoid<()>,
    tsBuf: Vec<SlicePack>,
    _pin: PhantomPinned,
}

impl TsMuxer {
    pub fn new() -> Pin<Box<Self>> {
        let mut pin_obj = Box::pin(Self {
            ts: HyVoid::from_ptr(unsafe { lcTsMCreate() }),
            tsBuf: Vec::new(),
            _pin: PhantomPinned,
        });

        let this = unsafe { self_mut_from_pinbox(&mut pin_obj) };

        unsafe {
            lcTsMInit(this.ts.as_ptr(), Some(comCb), HyVoid::from_ref(this).as_ptr());
        }
        pin_obj
    }
    pub fn enable_time_stamp(self: &mut Pin<Box<Self>>) {
        let this = unsafe { self_mut_from_pinbox(self) };

        let data = r#"
        {
            "use_time_stamp": true
        }"#;
        let config = CString::new(data).unwrap();
        unsafe {
            lcTsMConfig(
                this.ts.as_ptr(),
                config.as_bytes().as_ptr() as _,
                config.as_bytes().len() as _,
            );
        }
    }
    pub fn reset(self: &mut Pin<Box<Self>>) {
        let this = unsafe { self_mut_from_pinbox(self) };
        unsafe {
            lcTsMReset(this.ts.as_ptr());
        }
    }

    pub fn write<'a>(self: &'a mut Pin<Box<Self>>, data: &[u8], info: &CESStreamInfo) -> Result<Vec<&'a [u8]>> {
        let this = unsafe { self_mut_from_pinbox(self) };

        let ok = unsafe { lcTsMInputData(this.ts.as_ptr(), data.as_ptr(), data.len() as _, info as *const _) };
        if ok == 0 {
            bail!("Continue")
        }

        let mut v = Vec::new();
        for pack in &this.tsBuf {
            v.push(unsafe { std::slice::from_raw_parts(pack.ptr.as_ptr().cast(), pack.size) });
        }
        this.tsBuf.clear();
        Ok(v)
    }
}
impl Drop for TsMuxer {
    fn drop(&mut self) {
        unsafe {
            lcTsMDestroy(self.ts.as_ptr());
        }
    }
}