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

use anyhow::{bail, Result};

unsafe extern "C" fn comCb(data: *const c_uchar, len: ::std::os::raw::c_int, user: *mut ::std::os::raw::c_void) {
    let p = opacue_to_mut::<PsMuxer>(user.cast());
    p.psBuf.push(SlicePack {
        ptr: HyVoidConst::from_ptr(data.cast()),
        size: len as usize,
    });
}

pub struct PsMuxer {
    ctx: HyVoid<()>,
    psBuf: Vec<SlicePack>,
    _pin: PhantomPinned,
}

impl PsMuxer {
    pub fn new() -> Pin<Box<Self>> {
        let mut pin_obj = Box::pin(Self {
            ctx: HyVoid::from_ptr(unsafe { psMCreate() }),
            psBuf: Vec::new(),
            _pin: PhantomPinned,
        });
        let this = unsafe { self_mut_from_pinbox(&mut pin_obj) };
        unsafe {
            psMInit(this.ctx.as_ptr(), Some(comCb), HyVoid::from_ref(this).as_ptr());
        }
        pin_obj
    }

    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 { psMInputData(this.ctx.as_ptr(), data.as_ptr(), data.len() as _, info as *const _) };
        if ok == 0 {
            bail!("psM input data fail, continue")
        }
        let mut v = Vec::new();
        for pack in &this.psBuf {
            v.push(unsafe { std::slice::from_raw_parts(pack.ptr.as_ptr().cast(), pack.size) });
        }
        this.psBuf.clear();
        Ok(v)
    }
}
impl Drop for PsMuxer {
    fn drop(&mut self) {
        unsafe {
            psMDestroy(self.ctx.as_ptr());
        }
    }
}